How to Remove Client Secrets from Source Code: Part I (the code part)

I wanted to share the music4dance code with a potential employer, but I didn’t want to share all of the various keys and secrets that I use to shared login services, music search services and such.  Since I hadn’t previously shared the code with anyone other than people that I would trust to share the keys as well, I have been pretty sloppy with just checking them in with source code.  This is particularly nasty since that meant that if I actually wanted to share the git repo, I would not only have to clean up the code, but I’d have to change every one of the client secrets and passwords.

It’s pretty well documented in a number of places that a reasonable way to do this is to save such sensitive information in the environment and load it at runtime.  That way it will never get checked into the source code.  The problem that I haven’t seen a nice solution to online is dealing with a whole bunch of these at the same time.  There are two parts to this, the changing of the source code and setting up the environment on all the machines that need it.

The documented way to load an environment variable is straightforward:

public string MyKey => Environment.GetEnvironmentVariable("mykey");

And if there’s some possibility that the property is read multiple times (and generally there is), you can easily cache the variable (I love some of these new C# syntax enhancements).

public string MyKey => _myKey ?? 
    (_myKey = Environment.GetEnvironmentVariable("mykey"));
Private string _myKey;

Now, I’m doing this in a dozen or so places in my code, and almost all of them conform to a pattern where I am storing a pair of values.  Either client key and secret or username and password.  So I threw together a little class to handle this.

public abstract class CoreAuthentication
    protected abstract string Client { get; }
    public string ClientId => _clientId ?? 
        (_clientId = Environment.GetEnvironmentVariable(Client + "-client-id"));
    public string ClientSecret => _clientSecret ?? 
        (_clientSecret = Environment.GetEnvironmentVariable(Client + "-client-secret"));

    private string _clientId;
    private string _clientSecret;

The idea for this is that I had a number of specific authorization handlers for each of the music services that would extend this class and override the Client property to let CoreAuthentication know what environment variable to read.

Then for the simpler cases where I just needed to grab the info and use it to an OWIN security provider, I created a generic subclass that just takes the client name that I use as the base of the environment variable name.

public class EnvAuthentication : CoreAuthentication
    public EnvAuthentication(string client)
        Client = client;

    protected override string Client { get; }

I use the EnvAuthentication class in my startup authorization like so:

var fbenv = new EnvAuthentication("fb");
var fb = new FacebookAuthenticationOptions
    AppId = fbenv.ClientId, 
    AppSecret = fbenv.ClientSecret

Not rocket science, but a somewhat cleaner solution than having Environment.GetEnvironmentVariable calls all over my code.

I’ll attack the problem of loading up all of those environment variables next time. Especially the case of batch loading into the azure app service, which I found particularly vexing.

1 thought on “How to Remove Client Secrets from Source Code: Part I (the code part)”

Leave a Reply