# Using Discourse as an account provider and PBKDF2 problems

**URL:** https://meta.discourse.org/t/using-discourse-as-an-account-provider-and-pbkdf2-problems/287596
**Category:** Development
**Created:** [December 5, 2023, 4:22pm UTC](https://meta.discourse.org/t/using-discourse-as-an-account-provider-and-pbkdf2-problems/287596 "2023-12-05T16:22:08Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![polyzium](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/polyzium/32/344527_2.png) [@polyzium](https://meta.discourse.org/u/polyzium)
#### Post date: [December 5, 2023, 4:22pm UTC](https://meta.discourse.org/t/using-discourse-as-an-account-provider-and-pbkdf2-problems/287596/1 "2023-12-05T16:22:08Z")

</div>

Hi. This is probably a stupid question, but we have been migrating our forum from XenForo over to Discourse. We have a backend server for authorization which involves connecting to the database and verifying credentials against the users table.

XenForo’s bcrypt algorithm worked as expected, and without any hassle. When we migrated over to Discourse however, the PBKDF2 algorithm did not seem to match my expectations. Same exact password, same exact salt, same exact number of iterations and length, but the output hash is different.

I tried various different implementations of PBKDF2 algorithm, but they all output the exact same (different from Discourse’s) hash. Including my own implementation.  
I would rather avoid mechanisms like OAuth2 or SSO due to additional overhead and additional work that it imposes upon us.

Has anyone used Discourse for such use cases, and if you have, how did you solve this problem?

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [December 5, 2023, 10:50pm UTC](https://meta.discourse.org/t/using-discourse-as-an-account-provider-and-pbkdf2-problems/287596/2 "2023-12-05T22:50:03Z")

</div>

Are you using the migrate password plugin?

---

<div class="post-metadata">

### Author: ![polyzium](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/polyzium/32/344527_2.png) [@polyzium](https://meta.discourse.org/u/polyzium)
#### Post date: [December 5, 2023, 11:07pm UTC](https://meta.discourse.org/t/using-discourse-as-an-account-provider-and-pbkdf2-problems/287596/3 "2023-12-05T23:07:06Z")

</div>

No, at least not that I am aware of. are you talking about [this?](https://meta.discourse.org/t/migrated-password-hashes-support/19512)

---

<div class="post-metadata">

### Author: ![supermathie](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/supermathie/32/507518_2.png) [@supermathie](https://meta.discourse.org/u/supermathie)
#### Post date: [December 5, 2023, 11:38pm UTC](https://meta.discourse.org/t/using-discourse-as-an-account-provider-and-pbkdf2-problems/287596/4 "2023-12-05T23:38:58Z")

</div>

> [@polyzium](#):
>
> I tried various different implementations of PBKDF2 algorithm, but they all output the exact same (different from Discourse’s) hash.

Have you tried `openssl`’s implementation? That’s what we use (you can see it in `discourse/lib/pbkdf2.rb`).

As an example, after setting a user’s password to `swordfish#`:

```plaintext
discourse_development=# select password_hash, salt, password_algorithm from users where id=2;
-[RECORD 1]------+-----------------------------------------------------------------
password_hash | 67650523776bdc87ebcd2fc11719553c87b11e6c4da49806d9d5232460d2adc9
salt | 712ef44dd6fe6d6f0f1b6f702bb78459
password_algorithm | $pbkdf2-sha256$i=600000,l=32$

```

```plaintext
$ openssl kdf \
  -kdfopt pass:'swordfish#' \
  -kdfopt salt:712ef44dd6fe6d6f0f1b6f702bb78459 \
  -kdfopt digest:SHA2-256 \
  -kdfopt iter:600000 \
  -keylen 32 \
  PBKDF2 \
  | tr -d : | tr '[:upper:]' '[:lower:]'
67650523776bdc87ebcd2fc11719553c87b11e6c4da49806d9d5232460d2adc9

```

---

<div class="post-metadata">

### Author: ![polyzium](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/polyzium/32/344527_2.png) [@polyzium](https://meta.discourse.org/u/polyzium)
#### Post date: [December 6, 2023, 12:02am UTC](https://meta.discourse.org/t/using-discourse-as-an-account-provider-and-pbkdf2-problems/287596/5 "2023-12-06T00:02:06Z")

</div>

We primarily used Go’s crypto/bcrypt implementation for Xenforo. The same hashes from various pbkdf2 algorithm implementations suggests me that Go possibly stores strings or casts strings to bytes in a somewhat different way.

I’ll have to try that tomorrow (it’s late over here). If OpenSSL gives me the desired result, then I would have to seek OpenSSL bindings for Go, or I would have to switch to an entirely different language (that has OpenSSL bindings) for the backend.

---

<div class="post-metadata">

### Author: ![supermathie](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/supermathie/32/507518_2.png) [@supermathie](https://meta.discourse.org/u/supermathie)
#### Post date: [December 6, 2023, 12:04am UTC](https://meta.discourse.org/t/using-discourse-as-an-account-provider-and-pbkdf2-problems/287596/6 "2023-12-06T00:04:16Z")

</div>

Do you have a short example test case?

E.g. if you use the info above, what do you get?

---

<div class="post-metadata">

### Author: ![polyzium](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/polyzium/32/344527_2.png) [@polyzium](https://meta.discourse.org/u/polyzium)
#### Post date: [December 6, 2023, 12:09am UTC](https://meta.discourse.org/t/using-discourse-as-an-account-provider-and-pbkdf2-problems/287596/7 "2023-12-06T00:09:56Z")

</div>

Sorry, I am currently not in a position to tell you as timezones are annoying. It’s very late out here and I could only do so next day.

---

<div class="post-metadata">

### Author: ![polyzium](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/polyzium/32/344527_2.png) [@polyzium](https://meta.discourse.org/u/polyzium)
#### Post date: [December 6, 2023, 11:10am UTC](https://meta.discourse.org/t/using-discourse-as-an-account-provider-and-pbkdf2-problems/287596/8 "2023-12-06T11:10:06Z")

</div>

I did as you asked. The password is `swordfish#98765`.

Database entry:

```plaintext
discourse=> SELECT password_hash, salt, password_algorithm FROM users WHERE id=1;
                          password_hash | salt | password_algorithm       
------------------------------------------------------------------+----------------------------------+-------------------------------
 db3f0829e66336323e81110a1792a76000b9c60605e1fa6964797ea1b07c33c6 | 0d079078e220158011afaf497794166d | $pbkdf2-sha256$i=600000,l=32$
(1 row)

```

OpenSSL:

```plaintext
/var/discourse# openssl kdf \
> -kdfopt pass:'swordfish#98765' \
> -kdfopt salt:0d079078e220158011afaf497794166d \
> -kdfopt digest:SHA2-256 \
> -kdfopt iter:600000 \
> -keylen 32 \
> PBKDF2 \
> | tr -d : | tr '[:upper:]' '[:lower:]'
db3f0829e66336323e81110a1792a76000b9c60605e1fa6964797ea1b07c33c6

```

Go code:

```go
var userId int
var hash string
var salt string
var active bool

row := s.Database.QueryRow(`
	SELECT u.id, u.password_hash, u.salt, u.active
	FROM users AS u
	INNER JOIN user_emails AS ue ON u.id = ue.user_id
	WHERE ue.email = $1;`,
	email,
)

if err := row.Scan(&userId, &hash, &salt, &active); err != nil {
	// error handling...
}

hashBytes, err := hex.DecodeString(hash)
if err != nil {
	// error handling...
}

saltBytes, err := hex.DecodeString(salt)
if err != nil {
	// error handling...
}

key := pbkdf2.Key([]byte(password), saltBytes, 600000, 32, sha256.New)

fmt.Printf("salt: %v\n", salt)
fmt.Printf("hash: %v\n", hash)
fmt.Printf("hex.EncodeToString(key): %v\n", hex.EncodeToString(key))

```

Output of the above code:

```plaintext
salt: 0d079078e220158011afaf497794166d
hash: db3f0829e66336323e81110a1792a76000b9c60605e1fa6964797ea1b07c33c6
hex.EncodeToString(key): b378c12d96ac62a6099fc674d334f0793e6294f7927da0badc811e794a960802

```

---

<div class="post-metadata">

### Author: ![polyzium](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/polyzium/32/344527_2.png) [@polyzium](https://meta.discourse.org/u/polyzium)
#### Post date: [December 6, 2023, 9:20pm UTC](https://meta.discourse.org/t/using-discourse-as-an-account-provider-and-pbkdf2-problems/287596/9 "2023-12-06T21:20:31Z")

</div>

Nevermind. I had to use the hex representation of the salt as the argument, not the decoded salt like I was doing in the post above. Now the hashes are equal.

---

<div class="post-metadata">

### Author: ![supermathie](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/supermathie/32/507518_2.png) [@supermathie](https://meta.discourse.org/u/supermathie)
#### Post date: [December 7, 2023, 2:49am UTC](https://meta.discourse.org/t/using-discourse-as-an-account-provider-and-pbkdf2-problems/287596/10 "2023-12-07T02:49:04Z")

</div>

> [@polyzium](#):
>
> the hex representation of the salt as the argument

This was also my theory! I made the same mistake at first.

---

<div class="post-metadata">

### Author: ![system](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/system/32/443519_2.png) [@system](https://meta.discourse.org/u/system)
#### Post date: [January 6, 2024, 2:49am UTC](https://meta.discourse.org/t/using-discourse-as-an-account-provider-and-pbkdf2-problems/287596/11 "2024-01-06T02:49:51Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
