This article shows how to implement two factor authentication using Twilio and IdentityServer4 using Identity. On the Microsoft’s Two-factor authentication with SMS documentation, Twilio and ASPSMS are promoted, but any SMS provider can be used.
Code: https://github.com/damienbod/AspNetCoreID4External
Setting up Twilio
Create an account and login to https://www.twilio.com/
Now create a new phone number and use the Twilio documentation to set up your account to send SMS messages. You need the Account SID, Auth Token and the Phone number which are required in the application.
The phone number can be configured here:
https://www.twilio.com/console/phone-numbers/incoming
Adding the SMS support to IdentityServer4
Add the Twilio Nuget package to the IdentityServer4 project.
<PackageReference Include="Twilio" Version="5.5.2" />
The Twilio settings should be a secret, so these configuration properties are added to the app.settings.json file with dummy values. These can then be used for the deployments.
"TwilioSettings": { "Sid": "dummy", "Token": "dummy", "From": "dummy" }
A configuration class is then created so that the settings can be added to the DI.
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IdentityServerWithAspNetIdentity.Services { public class TwilioSettings { public string Sid { get; set; } public string Token { get; set; } public string From { get; set; } } }
Now the user secrets configuration needs to be setup on your dev PC. Right click the IdentityServer4 project and add the user secrets with the proper values which you can get from your Twilio account.
{ "MicrosoftClientId": "your_secret..", "MircosoftClientSecret": "your_secret..", "TwilioSettings": { "Sid": "your_secret..", "Token": "your_secret..", "From": "your_secret..", } }
The configuration class is then added to the DI in the Startup class ConfigureServices method.
var twilioSettings = Configuration.GetSection("TwilioSettings"); services.Configure<TwilioSettings>(twilioSettings);
Now the TwilioSettings can be added to the AuthMessageSender class which is defined in the MessageServices file, if using the IdentityServer4 samples.
private readonly TwilioSettings _twilioSettings; public AuthMessageSender(ILogger<AuthMessageSender> logger, IOptions<TwilioSettings> twilioSettings) { _logger = logger; _twilioSettings = twilioSettings.Value; }
This class is also added to the DI in the startup class.
services.AddTransient<ISmsSender, AuthMessageSender>();
Now the TwilioClient can be setup to send the SMS in the SendSmsAsync method.
public Task SendSmsAsync(string number, string message) { // Plug in your SMS service here to send a text message. _logger.LogInformation("SMS: {number}, Message: {message}", number, message); var sid = _twilioSettings.Sid; var token = _twilioSettings.Token; var from = _twilioSettings.From; TwilioClient.Init(sid, token); MessageResource.CreateAsync(new PhoneNumber(number), from: new PhoneNumber(from), body: message); return Task.FromResult(0); }
The SendCode.cshtml view can now be changed to send the SMS with the style, layout you prefer.
<form asp-controller="Account" asp-action="SendCode" asp-route-returnurl="@Model.ReturnUrl" method="post" class="form-horizontal"> <input asp-for="RememberMe" type="hidden" /> <input asp-for="SelectedProvider" type="hidden" value="Phone" /> <input asp-for="ReturnUrl" type="hidden" value="@Model.ReturnUrl" /> <div class="row"> <div class="col-md-8"> <button type="submit" class="btn btn-default">Send a verification code using SMS</button> </div> </div> </form>
In the VerifyCode.cshtml, the ReturnUrl from the model property must be added to the form as a hidden item, otherwise your client will not be redirected back to the calling app.
<form asp-controller="Account" asp-action="VerifyCode" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal"> <div asp-validation-summary="All" class="text-danger"></div> <input asp-for="Provider" type="hidden" /> <input asp-for="RememberMe" type="hidden" /> <input asp-for="ReturnUrl" type="hidden" value="@Model.ReturnUrl" /> <h4>@ViewData["Status"]</h4> <hr /> <div class="form-group"> <label asp-for="Code" class="col-md-2 control-label"></label> <div class="col-md-10"> <input asp-for="Code" class="form-control" /> <span asp-validation-for="Code" class="text-danger"></span> </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <div class="checkbox"> <input asp-for="RememberBrowser" /> <label asp-for="RememberBrowser"></label> </div> </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <button type="submit" class="btn btn-default">Submit</button> </div> </div> </form>
Testing the application
If using an existing client, you need to update the Identity in the database. Each user requires that the TwoFactoredEnabled field is set to true and a mobile phone needs to be set in the phone number field, (Or any phone which can accept SMS)
Now login with this user:
The user is redirected to the send SMS page. Click the send SMS button. This sends a SMS to the phone number defined in the Identity for the user trying to authenticate.
You should recieve an SMS. Enter the code in the verify view. If no SMS was sent, check your Twilio account logs.
After a successful code validation, the user is redirected back to the consent page for the client application. If not redirected, the return url was not set in the model.
Links:
https://docs.microsoft.com/en-us/aspnet/core/security/authentication/2fa
http://docs.identityserver.io/en/release/
https://www.twilio.com/use-cases/two-factor-authentication
