Current details about Discourse’s password storage system can be found in discourse/docs/SECURITY.md at main · discourse/discourse · GitHub. At the time of writing, we use PDKDF2-SHA256, with 64k iterations.
In some situations, you may want to export all of the hashed passwords from Discourse, and import them into another system. For example, you may be migrating from built-in Discourse authentication to a custom SSO system. Remember, it is impossible to extract the original passwords from the database, so your destination system needs to be capable of running the same hashing algorithm with the same parameters.
You can use data explorer to export the information in a computer-readable format:
SELECT id, username, salt, password_hash FROM users
This will export the data in the native Discourse format. The salt and password hash will be hex encoded.
Some external systems support the PHC string format which aims to be a cross-algorithm way to represent the output of a password hashing function. For pbkdf2-sha256, this string contains the algorithm type, the number of iterations, base64-encoded salt and base64-encoded hash. Fortunately, Postgres can handle all of this for us in a single query.
To generate PHC strings for each Discourse user, you can use a data explorer query like this:
SELECT id, username,
concat(
'$pbkdf2-sha256$i=64000,l=32$',
salt,
'$',
replace(encode(decode(password_hash, 'hex'), 'base64'), '=', '')
) as phc
FROM users
If you use Auth0 then you want this instead:
SELECT
user_emails.email,
users.active as email_verified,
concat(
'$pbkdf2-sha256$i=64000,l=32$',
replace(encode(users.salt::bytea, 'base64'), '=', ''),
'$',
replace(encode(decode(users.password_hash, 'hex'), 'base64'), '=', '')
) as password_hash
FROM users
INNER JOIN user_emails
ON users.id = user_emails.user_id
AND user_emails.primary IS TRUE
AND users.id > 0