Handling Self-Signed Certificates in Auth0 Custom Database Connections
Last Updated:
Overview
{ "error": "invalid_grant", "error_description": "self signed certificate" }
Applies To
- Custom database scripts
- Actions, hooks, rules, and other extensibility code
- Self-signed certificates
Cause
Solution
This involves manually comparing the fingerprint of the remote server certificate with the expected fingerprint of the self-signed certificate in the custom database script.
This solution also applies to other extensibility codes, such as actions.
NOTE: If the certificate changes, it will be necessary to change the fingerprint in the script.
Example implementations
HTTP Requests (using Axios)
const axios = require('axios');
const https = require('https');
const crypto = require('crypto');
function createAxiosInstance(pinnedFingerprint) {
return axios.create({
httpsAgent: new https.Agent({
rejectUnauthorized: true,
checkServerIdentity: (host, cert) => {
const fingerprint = crypto
.createHash('sha256')
.update(cert.raw)
.digest('hex')
.toUpperCase();
if (fingerprint !== pinnedFingerprint) {
throw new Error('Certificate verification failed: fingerprint mismatch');
}
}
})
});
}
function login(email, password, callback) {
const pinnedFingerprint = 'AABBCCDDEEFF00112233445566778899001122334455667788990011223344';
const axiosInstance = createAxiosInstance(pinnedFingerprint);
// rest of code
}
PostgreSQL
const { Client } = require('pg');
const crypto = require('crypto');
function createPgClient(config, pinnedFingerprint) {
return new Client({
...config,
ssl: {
rejectUnauthorized: true,
checkServerIdentity: (host, cert) => {
const fingerprint = crypto
.createHash('sha256')
.update(cert.raw)
.digest('hex')
.toUpperCase();
if (fingerprint !== pinnedFingerprint) {
throw new Error('Certificate verification failed: fingerprint mismatch');
}
}
}
});
}
function login(email, password, callback) {
const dbConfig = {
host: 'localhost',
port: 5432,
user: 'me',
password: 'secret',
database: 'mydb'
};
const pinnedFingerprint = 'AABBCCDDEEFF00112233445566778899001122334455667788990011223344';
const client = createPgClient(dbConfig, pinnedFingerprint);
// rest of code
}
MySQL
const mysql = require('mysql');
const crypto = require('crypto');
function createMySQLConnection(config, pinnedFingerprint) {
const connection = mysql.createConnection({
...config,
ssl: {
rejectUnauthorized: true
}
});
connection.on('secureConnect', () => {
const cert = connection.connection.getPeerCertificate();
const fingerprint = crypto
.createHash('sha256')
.update(cert.raw)
.digest('hex')
.toUpperCase();
if (fingerprint !== pinnedFingerprint) {
connection.destroy();
throw new Error('Certificate verification failed: fingerprint mismatch');
}
});
return connection;
}
function login(email, password, callback) {
const dbConfig = {
host: 'localhost',
user: 'me',
password: 'secret',
database: 'mydb'
};
const pinnedFingerprint = 'AABBCCDDEEFF00112233445566778899001122334455667788990011223344556677889900112233';
const connection = createMySQLConnection(dbConfig, pinnedFingerprint);
// rest of code
}
Retrieving the fingerprint
There are a couple of ways to retrieve the fingerprint. Since the output in both cases will contain sha256 Fingerprint=<fingerprint> and the fingerprint will be formatted with colons (':'), it will be necessary to remove these as well.- If there is direct access to the certificate file:
$ openssl x509 -in <.crt file> -noout -sha256 -fingerprint | sed -r 's/sha256 Fingerprint=(.+)/\1/' | tr -d ':'
- If there is network access only to the server:
$ openssl s_client -connect <server>:<port> < /dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256 | sed -r 's/sha256 Fingerprint=(.+)/\1/' | tr -d ':'