Sign in with an emailed code
Email-code sign-in stays off until you map it. AddPasswordAuth does not map these routes. AllowSignUp and the magic link default to off.
Request bodies, status codes, and option defaults are in the Email-code module.
Turn it on
- Map the entity and add a migration.
MapEmailCodeEndpointsthrowsNotSupportedExceptionifUseEmailCodes()was not called.protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.UseEmailCodes(); }
4.0 adds aPurposecolumn. Generate that migration before you deploy. See Upgrade to 4.0. - Register
IEmailCodeSender<TUser>. In Development, if you register none, AuthEndpoints logs the raw code withDevelopmentEmailCodeSender<TUser>. Outside Development, register your own sender. Startup fails when the sender isDevelopmentEmailCodeSender<TUser>, or when no sender is registered, unlessRequireSenderOutsideDevelopmentisfalse. - Register email-code services and map send and verify on one group. Pass the session that verify should finish in. A missing
AddEmailCodeEndpointsthrowsInvalidOperationExceptionwith the messageCall AddEmailCodeEndpoints before MapEmailCodeEndpoints.builder.Services.AddAuthEndpointsIdentity<AppUser, AppDbContext>(); builder.Services.AddCookieSessionEndpoints(); builder.Services.AddAccountManagementEndpoints(); builder.Services.AddEmailCodeEndpoints<AppUser, AppDbContext>(); var app = builder.Build(); app.UseAuthEndpoints(); var identity = app.MapGroup("/identity"); identity.MapEmailCodeEndpoints<AppUser>(AuthEndpointsSession.Cookie); identity.MapCookieSessionEndpoints<AppUser>(); identity.MapAccountManagementEndpoints<AppUser>();
A host that skips password register and login is Email-code only. That sample also turns the magic link on. - On the 3.x facade, set
EmailCode.Enabledtotrueinstead of mapping the group yourself. That map keeps Identity bearer verify. It ignoresSignInCompletionOptions.Default.builder.Services.AddAuthEndpoints<AppUser, AppDbContext>(o => { o.EmailCode.Enabled = true; });
To issue a JWT, call AddJwtEndpoints, then pass AuthEndpointsSession.Jwt to MapEmailCodeEndpoints. Jwt.Enabled on the 3.x facade does not change this session. The obsolete completer types are in Upgrade to 4.0. Session results are in Sign-in completer.
Send a code
Send POST /identity/emailCode/send with { "email" }. A valid address returns 200 with { "expiresInSeconds", "resendAfterSeconds" }. The defaults are 10 minutes and 60 seconds.
The same 200 is returned for an unknown address, a locked account, and several other cases. The list is in the Email-code module.
Code is not HTML-encoded. Do not log Code or LinkUrl. POST /emailCode/send awaits SendSignInCodeAsync. Queue the message and return. A sender that waits on SMTP makes a known address slower than an unknown one.
Verify a code
Send POST /identity/emailCode/verify with { "email", "code" }. code is exactly CodeLength digits. The default length is 6.
For the cookie session in the sample, add ?useSessionCookies=true for a session cookie, or ?useSessionCookies=false for a persistent cookie. useCookies is ignored. The 200 body is empty. Identity bearer and JWT flags are in the Email-code module.
A correct code for an eligible confirmed user returns 200. Then call GET /identity/manage/info.
Wrong codes, a required second factor, and 405 on GET /emailCode/verify are in the Email-code module and Responses and errors.
Create an account from the code
Set AllowSignUp to true on AddEmailCodeEndpoints. On the 3.x facade, set it inside EmailCode.Configure. An unknown address is then mailed with purpose SignUp. The 200 send body does not change.
A correct code creates one user and signs that user in. The user fields, and what happens to an unconfirmed account, are in the Email-code module.
With AllowSignUp left at false, a correct code for an unknown address returns 401 and creates no user.
Use a magic link
The magic link is off until you set MagicLink.Enabled to true. Set LinkUri, and in Production set AllowedOrigins. The URI rules are in the Email-code module.
The default link puts the token in the fragment. The fragment is not sent to your server. Read token in the client and post only that field:
{ "token": "…" }
A body that includes token together with email or code returns 400. A token-only body is 400 while the magic link is off, and the row is not consumed.
One successful verify consumes the row. A second verify of the same code or token returns 401.