I've built a program in C# that allows my end users to update the firmware. I did it by reverse engineering the Dufse program that can be found here. All the source code is included. This link describes the protocol.
My board has a switch on the boot0 pin that allows the MCU to boot into "system memory". When booting into "system memory", the MCU's USB interface will then run as a DFU device. You'll need to install the ST DFU drivers on your host PC, and then you can use the Dfuse program or your own program to update the firmware.
I also found this discussion useful, but don't let it lead you astray. The ST docs are the real source of information.
There are other ways as well. Did you know your firmware can actually write to flash memory? You could write your own boot loader that uses a USART, USB, Ethernet, or some other peripheral to program the device by writing to the flash. Be careful not to overwrite your boot loader though. ST seems to call this "in-application programming" (IAP).
These might be for different MCUs, but the principles are the same.
Hope this will give you the start you are looking for.