I recently worked on a project which needed to interface five hospital systems to one national system. As part of the SOAP submission to the national system I needed to provide a unique UUID which would identify each user from the source hospital system. I wanted to generate the UUID on the fly as the message was being created - so I needed a way to regenerate the same UUID for the same user.
Each hospital system had their own unique user account reference. So by combining this unique account reference with an organisation identifier, I could generate a unique UUID for users with the same account reference across different facilities.
This is the function I came up with.
Updated (11.03.2025)
- Switched from MD5 to XXH3 as suggested by StephaneBunel
- Swiched to Google's UUID package.
func deterministicGUID(organisation string, account string) uuid.UUID {
var hash [16]byte
var guid uuid.UUID
hash = xxh3.HashString128(organisation + account).Bytes()
// uuid.FromBytes returns an error if the slice
// of bytes is not 16 - as hash is defined as
// [16]byte then we can ignore checking the error
guid, _ = uuid.FromBytes(hash[:])
return guid
}
package main
import (
"fmt"
"github.com/google/uuid"
"github.com/zeebo/xxh3"
)
func main() {
fmt.Println("UUID for user 357 @ LTH : ", deterministicGUID("LTH", "357").String())
fmt.Println("UUID for user 5689 @ BHT : ", deterministicGUID("BHT", "5689").String())
}
UUID for user 357 @ LTH : c737ccd9-62da-f7b0-a6ad-6d21573b72e7
UUID for user 5689 @ BHT : 78e67353-64c6-2507-5683-4612bddcfe6f
Simply calling uuid.FromBytes may result in a UUID that does not pass muster if there is a strict UUID validator in your system. Some of the bits in a UUID actually have meaning and if you're simply presenting a checksum in the format of a UUID (which is what uuid.FromBytes will do) then you're likely to not be conforming to the RFC.
If, for some reason you don't want to use one of the methods in the RFC that allow deterministic UUID generation, you can use
uuid.NewRandomFromReader(bytes.NewReader(checksumbytes))
- this will essentially do the same thing as FromBytes, and ensure the UUID version/variant bits are set correctly.