Skip to content
This repository has been archived by the owner on Dec 20, 2018. It is now read-only.

Confirm Email Token Expiration/Lifetime #859

Closed
lcalabrese opened this issue Jun 7, 2016 · 18 comments
Closed

Confirm Email Token Expiration/Lifetime #859

lcalabrese opened this issue Jun 7, 2016 · 18 comments

Comments

@lcalabrese
Copy link
Contributor

Currently the different types of token (Confirm Email/Password Reset/Change Email) use the same shared DataProtectorTokenProvider named "Default" which in turn all share the same DataProtectionTokenProviderOptions. This means you are unable to configure the lifetime of each type of token separately. Ideally I would like to be able to set the Password Reset to something like 4 hours and the Confirm Email token to 7 days.

I believe I can get around this by subclassing DataProtectorTokenProvider and DataProtectionTokenProviderOptions, and setting the appropriate name via IdentityOptions.Token.EmailConfirmationTokenProvider but it is far from a pretty solution.

@HaoK
Copy link
Member

HaoK commented Jun 7, 2016

Alternatively you can set them to be different token providers via https://github.com/aspnet/Identity/blob/dev/src/Microsoft.AspNetCore.Identity/TokenOptions.cs

@lcalabrese
Copy link
Contributor Author

That's what I ended up doing, but in order to have a different value for the injected DataProtectionTokenProviderOptions.TokenLifespan, I need to have them actually be different classes and not simply another instance of DataProtectorTokenProvider with a different Name. This way I can get the DI container to grab the correct options based on the new class's constructor. Am I misunderstanding the Options model?

Here's what I ended up doing; is this what you were referring to or is there actually a simpler way?

public class ConfirmEmailDataProtectorTokenProvider<TUser>: DataProtectorTokenProvider<TUser> where TUser:class
{
    public ConfirmEmailDataProtectorTokenProvider(IDataProtectionProvider dataProtectionProvider, IOptions<ConfirmEmailDataProtectionTokenProviderOptions> options) : base(dataProtectionProvider, options)
    {
    }
}

public class ConfirmEmailDataProtectionTokenProviderOptions : DataProtectionTokenProviderOptions { }

public class Startup
{
    private const string EmailConfirmationTokenProviderName = "ConfirmEmail";

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<IdentityOptions>(options =>
        {
            options.Tokens.EmailConfirmationTokenProvider = EmailConfirmationTokenProviderName;
        });

        services.Configure<ConfirmEmailDataProtectionTokenProviderOptions>(options =>
        {
            options.TokenLifespan = TimeSpan.FromDays(7);
        });

        services.AddIdentity<MyUser, MyRole>()
            .AddEntityFrameworkStores<MyDbContext, string>()
            .AddDefaultTokenProviders()
            .AddTokenProvider<ConfirmEmailDataProtectorTokenProvider<MyUser>>(EmailConfirmationTokenProviderName);
    }
}

@HaoK
Copy link
Member

HaoK commented Jun 7, 2016

Ah yeah you can't have different options for the same class no, see related #465

@Ro3A
Copy link

Ro3A commented Jun 14, 2016

Thanks guys for the work around. Just wanted to add a scenario and a voice here. An internal employee added a user to our site on Friday and triggering an activation email for the new user. New user didn't click the activation link until Monday. Token expired. It would be ideal to have an easy options configuration similar to how AddIdentity is done.

@lcalabrese
Copy link
Contributor Author

Indeed and that’s a pretty similar scenario to what we’ve run into. We have a system set up where administrators can “invite” new users to the site, which basically generates a confirm email token (technically I’m using a different Purpose string but it’s not relevant) and sends an invitation email out to the new user. Then for whatever reason they don’t check their email until later - it might be a Friday and they don’t see it until Monday, or they might happen to be on vacation, or their IT department may not have even given them access to their email account yet.

In addition, I think it would be great if the documentation / new project template said something about this. The new project template’s AccountController simply indicates to uncomment a few lines to get users to confirm their email. Granted most new users will want to start using the site right away and will check immediately, but this still leaves a potential for accounts to be left “in limbo”.

In this case, with the default boilerplate code, they will (hopefully) contact support, but even then the site owner/administrator can only apologize, delete the half-set-up account, and say “try again”. An ugly user experience at best. (I suppose the other option is to manually set the user’s email-confirmed flag, then direct them to the “forgot password” form – but this is not security-ideal unless we make sure to manually confirm the email loop.)

To avoid this we need a way to re-send out a token after it’s expired. Perhaps ideally this would be in response to “invalid token” when the user clicks an expired link. (Because we were getting so many support calls about this one I’ve gone a different route and created an admin controller action that accepts an email address, so we can re-send emails with tokens directly.) Either way having at least a note in the documentation that this might be necessary would bring this to light earlier.

Ultimately this is all one man’s opinion, but I just wanted to get it on record. If I had more time I’d offer to write something up, but unfortunately I’m not at that stage yet.

From: Ro3A [mailto:[email protected]]
Sent: Tuesday, June 14, 2016 10:54 PM
To: aspnet/Identity [email protected]
Cc: Lee Calabrese [email protected]; Author [email protected]
Subject: Re: [aspnet/Identity] Confirm Email Token Expiration/Lifetime (#859)

Thanks guys for the work around here. Just wanted to add a scenario and a voice here. An internal employee added a user to our site on Friday and triggering an activation email for the new user. New user didn't click the activation link until Monday. Token expired. It would be ideal to have an easy options configuration similar to how AddIdentity is done.


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub #859 (comment) , or mute the thread https://github.com/notifications/unsubscribe/ANDcwZ_6WFhwATYrnHH8aGAID1a4VAnYks5qLrJrgaJpZM4Ive0_ . https://github.com/notifications/beacon/ANDcwbHCGAebq86oCR9Np4ZtDvxpDYMJks5qLrJrgaJpZM4Ive0_.gif

@blowdart
Copy link
Member

See also #465

@HaoK
Copy link
Member

HaoK commented Sep 20, 2016

Short term plan is to add TokenProviderInstance to the TypeDescriber map, so an instance can be jammed in via IdentiyOptions configuration

@Gillardo
Copy link

Any news on all this? Has it all been done so i can change the tokenLifeTime for just the EmailConfirmation token??

@dotnetshadow
Copy link

Yes do you have example code on how to do this?

How do you change the message from simply "Invalid Token" ?

@HaoK
Copy link
Member

HaoK commented Jan 27, 2017

You should be able to plug in your own instance of a token provider like this: https://github.com/aspnet/Identity/pull/983/files#diff-1f767f1c742b161c6a1650ec072addeeR645

@jrgunawan
Copy link

@HaoK Could you please give code sample on how to change the TokenLifeSpan utilizing the change on TokenProviderDescriptor?

My understanding when looking at https://github.com/aspnet/Identity/pull/983/files#diff-1f767f1c742b161c6a1650ec072addeeR645 , I need to create an instance of DataProtectorTokenProvider (because I want to reuse IUserTwoFactorTokenProvider implementation), modify its DataProtectionTokenProviderOptions.TokenLifeSpan, then assign that instance to the ProviderInstance.

Am I on the right track? Currently, I am still using @lcalabrese 's implementation.

@snjay
Copy link

snjay commented Mar 10, 2017

Also looking for a code sample to change the TokenLifeSpan. Have there been any updates?

@HaoK
Copy link
Member

HaoK commented Mar 10, 2017

Yup @jrgunawan that change allows you to specify a instance with the appropriate token life span

@Gillardo
Copy link

Any chance someone has code sample to show this?

@VahidN
Copy link

VahidN commented Mar 10, 2017

Just follow the posted answer by @lcalabrese. It changes the default TokenLifespan to 7 days.

@mskurnik
Copy link

For me I had to add an a little bit more info when adding the identity in order to force the new token provider on password create and update.

services.AddIdentity<ApplicationUser, IdentityRole>(
    options =>
    {
        options.Tokens.PasswordResetTokenProvider = EmailConfirmationTokenProviderName;
        options.Tokens.EmailConfirmationTokenProvider = EmailConfirmationTokenProviderName;
    })
    .AddEntityFrameworkStores<PortalDbContext>()
    .AddDefaultTokenProviders()
    .AddTokenProvider<ConfirmEmailDataProtectorTokenProvider<ApplicationUser>>(EmailConfirmationTokenProviderName);

@ngnam
Copy link

ngnam commented Jul 16, 2018

thanks !
but i want know why must use
using static App.ConfirmEmailDataProtectorTokenProvider;

 services.Configure<ConfirmEmailDataProtectionTokenProviderOptions>(options =>
        {
            options.TokenLifespan = TimeSpan.FromMinutes(5);
        });

@willdaviesdevelops
Copy link

The previous cited url of "https://github.com/aspnet/Identity/blob/dev/src/Microsoft.AspNetCore.Identity/TokenOptions.cs" is broken (404).
Consider this link as an alternative: TokenOptions.cs

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests