I wanted a quick Magic Link implementation for my Blazor app, so I cobbled together a solution. I took inspiration from a NuGet package (forgot which one), but it’s simple enough to do with a few methods.
When a user decides to log in with a magic link, you can call a method like this. I’m loading a lot of the email server config elsewhere in the service.
12345678910111213141516171819202122
publicasyncTask<string?>GenerateMagicLinkAsync(stringuserId){vartoken=GenerateToken(userId);// the actual encoded JWT tokenvarbaseUrl=_options.MagicLinkBaseUrl;// the base URL of the app stored in configvarmagicLink=$"{baseUrl}?i={token}";// the magic link formatted for the controllervaruser=awaituserManager.FindByEmailAsync(userId);// get the email address of the userIdvaremail=newMimeMessage();email.To.Add(newMailboxAddress("",user.Email));email.From.Add(newMailboxAddress(_options.EmailFromName,_options.EmailFromAddress));email.Subject="Login";email.Body=newTextPart("html"){Text=$"Click the following link to sign in: <br/><a href=\"{magicLink}\">{magicLink}</a>"};usingvarclient=newSmtpClient();// load the details fo the SMTP server from configawaitclient.ConnectAsync(_options.MailServer,_options.MailPort,_options.MailUseSsl);awaitclient.AuthenticateAsync(_options.MailUsername,_options.MailPassword);awaitclient.SendAsync(email);awaitclient.DisconnectAsync(true);returnmagicLink;}
So the magic piece of the magic link is a JWT token which signs the information about the authentication and the user with a secret key specific to your app:
1234567891011121314151617181920
publicstringGenerateToken(stringuserId){varsecretKey=newSymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.SecretKey));// secret key to decode the JWTvarsigningCredentials=newSigningCredentials(secretKey,SecurityAlgorithms.HmacSha256);varclaims=newList<Claim>{newClaim(ClaimTypes.NameIdentifier,userId)};vartokenDescriptor=newSecurityTokenDescriptor{Issuer=_options.Issuer,Audience=_options.Audience,Expires=DateTime.UtcNow.AddMinutes(_options.TokenExpirationMinutes),SigningCredentials=signingCredentials,Claims=claims.ToDictionary(c=>c.Type,c=>(object)c.Value),IssuedAt=DateTime.UtcNow,};vartokenHandler=newJwtSecurityTokenHandler();varsecurityToken=tokenHandler.CreateToken(tokenDescriptor);returntokenHandler.WriteToken(securityToken);}
When the user clicks the magic link, the app responds from this minimal API in program.cs:
In conclusion, implementing a Magic Link authentication feature in a Blazor app can be achieved by generating a JWT token as the magic link, sending it via email, and validating it when the user clicks the link. The GenerateMagicLinkAsync method generates the magic link and sends it to the user’s email using SMTP. The GenerateToken method creates a JWT token with the necessary claims and signing it with a secret key. The minimal API in program.cs handles the redirection based on the validity of the magic link. The ValidateMagicLinkAsync method validates the token and signs in the user if it’s valid. Finally, the ValidateToken method decrypts and validates the JWT token. With these components in place, you can provide a seamless and secure authentication experience for your Blazor app users.