0

I have a library for sending email within software (a wrapper to MailKit). The parameters for the email process (server, user name, password, etc) are defined within appsetting.json:

  "DpEmail": {
    "SMTP": {
      "Host_Address": "mail.myServer.net",
      "Host_Port": "465",
      "Host_UserName": "a@b.c",
      "Host_Password": "thePassword",
      "Sender_Email": "noreply@myDomain.eu",
      "Sender_Name": "NoReply"
    }
  },

I define a class to contain the options:

public class MailSenderOptions { //mirrors elements of appsettings.json section }

And set up the mail service using the options pattern:

services.Configure<MailSenderOptions> (GetSection ("DpEmail:SMTP"));
services.AddSingleton<IDpEmail, DpMailLib.DpEmail> ();

This all works fine for development but, in production, for security I would want to provide passwords from other sources (eg app arguments, environment variables). The same would apply to database passwords, etc.

My question is how, using best practice, I could intervene in the Options Pattern, to set the password in the MailSenderOptions instance within DI that I have obtained securely from a source other than the appsettings file, while the non-confidential items come from that file. Is there a sensible alternative to injecting the password separately?

David
  • 657
  • 5
  • 13

1 Answers1

1

You can use IPostConfigureOptions for this:

Registration

services.Configure<MailSenderOptions>(GetSection("DpEmail:SMTP"));
services.AddTransient<IPostConfigureOptions<MailSenderOptions>, PostConfigureMailSenderOptions>();

Additional options

private class PostConfigureMailSenderOptions : IPostConfigureOptions<MailSenderOptions>
{
    private readonly IHostApplicationLifetime _applicationLifetime;

    public PostConfigureMailSenderOptions(IHostApplicationLifetime applicationLifetime)
    {
        _applicationLifetime = applicationLifetime;
        // You can inject whatever services you need to via the constructor
        // I've injected IHostApplicationLifetime as an example
    }

    public void PostConfigure(string? name, MailSenderOptions options)
    {
        // make changes to the existing MailSenderOptions configuration
        // if you have named options then you'll want to pay attention
        // to name. In your case you don't need to.
    }
}

Now, after the initial configuration runs, your PostConfigureMailSenderOptions class' PostConfigure method will run against the same configuration.

Using this, you could have a service that allows you to read the secure credentials from your preferred services.

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86