2013-10-15 18:46:44 +11:00
using System ;
using System.Collections.Generic ;
2015-02-20 14:17:28 +01:00
using System.Linq ;
2013-08-12 15:06:12 +02:00
using System.Net ;
using System.Net.Http ;
2015-02-20 14:17:28 +01:00
using System.Threading.Tasks ;
2013-08-12 15:06:12 +02:00
using System.Web ;
using System.Web.Http ;
2016-04-13 13:51:12 +02:00
using System.Web.Mvc ;
2013-08-26 11:28:15 +10:00
using AutoMapper ;
2015-02-06 13:47:00 +11:00
using Microsoft.AspNet.Identity ;
2016-04-12 18:07:25 +02:00
using Microsoft.AspNet.Identity.Owin ;
2015-02-06 13:47:00 +11:00
using Microsoft.Owin ;
2013-10-15 18:46:44 +11:00
using Umbraco.Core ;
2013-11-20 14:18:03 +11:00
using Umbraco.Core.Configuration ;
2016-04-12 18:07:25 +02:00
using Umbraco.Core.Logging ;
2016-04-13 14:35:40 +02:00
using Umbraco.Core.Models ;
2016-08-12 12:20:00 +02:00
using Umbraco.Core.Models.Identity ;
2016-04-12 18:07:25 +02:00
using Umbraco.Core.Security ;
using Umbraco.Core.Services ;
2013-12-11 18:53:00 +11:00
using Umbraco.Web.Models ;
2013-08-12 15:06:12 +02:00
using Umbraco.Web.Models.ContentEditing ;
using Umbraco.Web.Mvc ;
using Umbraco.Web.Security ;
2016-04-12 18:07:25 +02:00
using Umbraco.Web.Security.Identity ;
2013-08-12 15:06:12 +02:00
using Umbraco.Web.WebApi ;
using Umbraco.Web.WebApi.Filters ;
2015-03-25 10:57:10 +11:00
using IUser = Umbraco . Core . Models . Membership . IUser ;
2013-08-12 15:06:12 +02:00
namespace Umbraco.Web.Editors
{
/// <summary>
/// The API controller used for editing content
/// </summary>
[PluginController("UmbracoApi")]
[ValidationFilter]
2013-12-06 15:01:58 +11:00
[AngularJsonOnlyConfiguration]
2014-01-15 13:49:37 +11:00
[IsBackOffice]
2013-08-12 15:06:12 +02:00
public class AuthenticationController : UmbracoApiController
{
2015-03-24 20:17:37 +11:00
2016-08-12 12:20:00 +02:00
private BackOfficeUserManager < BackOfficeIdentityUser > _userManager ;
2015-07-01 17:07:29 +02:00
private BackOfficeSignInManager _signInManager ;
2016-08-12 12:20:00 +02:00
protected BackOfficeUserManager < BackOfficeIdentityUser > UserManager
2015-03-24 20:17:37 +11:00
{
2016-08-12 12:20:00 +02:00
get { return _userManager ? ? ( _userManager = TryGetOwinContext ( ) . Result . GetBackOfficeUserManager ( ) ) ; }
2015-03-24 20:17:37 +11:00
}
2015-07-01 17:07:29 +02:00
protected BackOfficeSignInManager SignInManager
{
2016-08-12 12:20:00 +02:00
get { return _signInManager ? ? ( _signInManager = TryGetOwinContext ( ) . Result . GetBackOfficeSignInManager ( ) ) ; }
2015-07-01 17:07:29 +02:00
}
2017-06-13 18:38:16 +02:00
2017-06-15 12:45:53 +02:00
/// <summary>
/// Returns the configuration for the backoffice user membership provider - used to configure the change password dialog
/// </summary>
/// <returns></returns>
[WebApi.UmbracoAuthorize(requireApproval: false)]
public IDictionary < string , object > GetMembershipProviderConfig ( )
{
var provider = Core . Security . MembershipProviderExtensions . GetUsersMembershipProvider ( ) ;
return provider . GetConfiguration ( Services . UserService ) ;
}
2017-05-29 15:55:36 +02:00
/// <summary>
2017-06-13 18:38:16 +02:00
/// Checks if a valid token is specified for an invited user and if so logs the user in and returns the user object
2017-05-29 15:55:36 +02:00
/// </summary>
2017-06-13 18:38:16 +02:00
/// <param name="id"></param>
2017-05-29 15:55:36 +02:00
/// <param name="token"></param>
/// <returns></returns>
2017-06-13 18:38:16 +02:00
/// <remarks>
/// This will also update the security stamp for the user so it can only be used once
/// </remarks>
2017-05-29 15:55:36 +02:00
[ValidateAngularAntiForgeryToken]
2017-06-13 18:38:16 +02:00
public async Task < UserDisplay > PostVerifyInvite ( [ FromUri ] int id , [ FromUri ] string token )
2017-05-29 15:55:36 +02:00
{
if ( string . IsNullOrWhiteSpace ( token ) )
throw new HttpResponseException ( Request . CreateResponse ( HttpStatusCode . NotFound ) ) ;
var decoded = token . FromUrlBase64 ( ) ;
if ( decoded . IsNullOrWhiteSpace ( ) )
2017-06-13 18:38:16 +02:00
throw new HttpResponseException ( Request . CreateResponse ( HttpStatusCode . NotFound ) ) ;
2017-05-29 15:55:36 +02:00
2017-06-13 18:47:20 +02:00
var identityUser = await UserManager . FindByIdAsync ( id ) ;
if ( identityUser = = null )
2017-05-29 15:55:36 +02:00
throw new HttpResponseException ( Request . CreateResponse ( HttpStatusCode . NotFound ) ) ;
2015-07-01 17:07:29 +02:00
2017-06-13 18:38:16 +02:00
var result = await UserManager . ConfirmEmailAsync ( id , decoded ) ;
if ( result . Succeeded = = false )
2017-06-13 18:47:20 +02:00
{
2017-06-13 18:38:16 +02:00
throw new HttpResponseException ( Request . CreateNotificationValidationErrorResponse ( string . Join ( ", " , result . Errors ) ) ) ;
2017-06-13 18:47:20 +02:00
}
Request . TryGetOwinContext ( ) . Result . Authentication . SignOut (
Core . Constants . Security . BackOfficeAuthenticationType ,
Core . Constants . Security . BackOfficeExternalAuthenticationType ) ;
await SignInManager . SignInAsync ( identityUser , false , false ) ;
var user = ApplicationContext . Services . UserService . GetUserById ( id ) ;
2017-06-13 18:38:16 +02:00
2017-05-29 15:55:36 +02:00
return Mapper . Map < UserDisplay > ( user ) ;
}
2015-02-20 14:17:28 +01:00
[WebApi.UmbracoAuthorize]
[ValidateAngularAntiForgeryToken]
public async Task < HttpResponseMessage > PostUnLinkLogin ( UnLinkLoginModel unlinkLoginModel )
{
var result = await UserManager . RemoveLoginAsync (
User . Identity . GetUserId < int > ( ) ,
new UserLoginInfo ( unlinkLoginModel . LoginProvider , unlinkLoginModel . ProviderKey ) ) ;
if ( result . Succeeded )
{
var user = await UserManager . FindByIdAsync ( User . Identity . GetUserId < int > ( ) ) ;
2015-12-15 10:34:11 +01:00
await SignInManager . SignInAsync ( user , isPersistent : true , rememberBrowser : false ) ;
2015-02-20 14:17:28 +01:00
return Request . CreateResponse ( HttpStatusCode . OK ) ;
}
else
{
AddModelErrors ( result ) ;
return Request . CreateValidationErrorResponse ( ModelState ) ;
}
}
2013-08-12 15:06:12 +02:00
/// <summary>
2013-10-01 13:23:13 +10:00
/// Checks if the current user's cookie is valid and if so returns OK or a 400 (BadRequest)
/// </summary>
/// <returns></returns>
2016-04-13 13:51:12 +02:00
[System.Web.Http.HttpGet]
2014-05-13 13:39:18 +10:00
public bool IsAuthenticated ( )
2013-10-01 13:23:13 +10:00
{
var attempt = UmbracoContext . Security . AuthorizeRequest ( ) ;
if ( attempt = = ValidateRequestAttempt . Success )
{
2014-05-13 13:39:18 +10:00
return true ;
}
return false ;
2013-10-01 13:23:13 +10:00
}
/// <summary>
2013-10-15 18:46:44 +11:00
/// Returns the currently logged in Umbraco user
2013-08-12 15:06:12 +02:00
/// </summary>
/// <returns></returns>
2013-12-03 11:57:41 +11:00
/// <remarks>
/// We have the attribute [SetAngularAntiForgeryTokens] applied because this method is called initially to determine if the user
/// is valid before the login screen is displayed. The Auth cookie can be persisted for up to a day but the csrf cookies are only session
/// cookies which means that the auth cookie could be valid but the csrf cookies are no longer there, in that case we need to re-set the csrf cookies.
/// </remarks>
2013-10-15 18:46:44 +11:00
[WebApi.UmbracoAuthorize]
2017-07-19 19:22:43 +10:00
[SetAngularAntiForgeryTokens]
2017-07-25 20:51:58 +10:00
[CheckIfUserTicketDataIsStale]
2015-02-22 13:29:00 +01:00
public UserDetail GetCurrentUser ( )
2013-08-12 15:06:12 +02:00
{
2017-06-27 19:55:03 +10:00
var user = UmbracoContext . Security . CurrentUser ;
2017-06-14 16:21:56 +02:00
var result = Mapper . Map < UserDetail > ( user ) ;
var httpContextAttempt = TryGetHttpContext ( ) ;
if ( httpContextAttempt . Success )
{
//set their remaining seconds
result . SecondsUntilTimeout = httpContextAttempt . Result . GetRemainingAuthSeconds ( ) ;
}
return result ;
}
/// <summary>
/// When a user is invited they are not approved but we need to resolve the partially logged on (non approved)
/// user.
/// </summary>
/// <returns></returns>
/// <remarks>
/// We cannot user GetCurrentUser since that requires they are approved, this is the same as GetCurrentUser but doesn't require them to be approved
/// </remarks>
[WebApi.UmbracoAuthorize(requireApproval:false)]
[SetAngularAntiForgeryTokens]
public UserDetail GetCurrentInvitedUser ( )
{
2017-06-27 19:55:03 +10:00
var user = UmbracoContext . Security . CurrentUser ;
if ( user . IsApproved )
{
//if they are approved, than they are no longer invited and we can return an error
throw new HttpResponseException ( Request . CreateUserNoAccessResponse ( ) ) ;
}
2013-10-15 18:46:44 +11:00
var result = Mapper . Map < UserDetail > ( user ) ;
var httpContextAttempt = TryGetHttpContext ( ) ;
if ( httpContextAttempt . Success )
2013-08-12 15:06:12 +02:00
{
2013-10-15 18:46:44 +11:00
//set their remaining seconds
result . SecondsUntilTimeout = httpContextAttempt . Result . GetRemainingAuthSeconds ( ) ;
2013-08-12 15:06:12 +02:00
}
2015-02-20 14:17:28 +01:00
2013-10-15 18:46:44 +11:00
return result ;
2013-08-12 15:06:12 +02:00
}
2017-07-19 19:22:43 +10:00
//TODO: This should be on the CurrentUserController?
2015-02-22 13:29:00 +01:00
[WebApi.UmbracoAuthorize]
2015-03-24 20:17:37 +11:00
[ValidateAngularAntiForgeryToken]
2015-02-22 13:29:00 +01:00
public async Task < Dictionary < string , string > > GetCurrentUserLinkedLogins ( )
{
var identityUser = await UserManager . FindByIdAsync ( UmbracoContext . Security . GetUserId ( ) ) ;
return identityUser . Logins . ToDictionary ( x = > x . LoginProvider , x = > x . ProviderKey ) ;
}
2013-08-12 15:06:12 +02:00
/// <summary>
/// Logs a user in
/// </summary>
/// <returns></returns>
2013-12-03 11:36:17 +11:00
[SetAngularAntiForgeryTokens]
2015-03-25 10:57:10 +11:00
public async Task < HttpResponseMessage > PostLogin ( LoginModel loginModel )
2013-08-12 15:06:12 +02:00
{
2016-04-12 18:07:25 +02:00
var http = EnsureHttpContext ( ) ;
2015-05-10 17:47:32 +02:00
2015-07-01 17:07:29 +02:00
var result = await SignInManager . PasswordSignInAsync (
loginModel . Username , loginModel . Password , isPersistent : true , shouldLockout : true ) ;
switch ( result )
2013-08-12 15:06:12 +02:00
{
2015-07-01 17:07:29 +02:00
case SignInStatus . Success :
//get the user
var user = Security . GetBackOfficeUser ( loginModel . Username ) ;
2017-02-03 00:47:28 +11:00
return SetPrincipalAndReturnUserDetail ( user ) ;
2015-07-01 17:07:29 +02:00
case SignInStatus . RequiresVerification :
var twofactorOptions = UserManager as IUmbracoBackOfficeTwoFactorOptions ;
if ( twofactorOptions = = null )
{
throw new HttpResponseException (
Request . CreateErrorResponse (
HttpStatusCode . BadRequest ,
"UserManager does not implement " + typeof ( IUmbracoBackOfficeTwoFactorOptions ) ) ) ;
}
2017-02-02 22:11:34 +11:00
2015-07-01 17:07:29 +02:00
var twofactorView = twofactorOptions . GetTwoFactorView (
TryGetOwinContext ( ) . Result ,
UmbracoContext ,
loginModel . Username ) ;
if ( twofactorView . IsNullOrWhiteSpace ( ) )
{
throw new HttpResponseException (
Request . CreateErrorResponse (
HttpStatusCode . BadRequest ,
typeof ( IUmbracoBackOfficeTwoFactorOptions ) + ".GetTwoFactorView returned an empty string" ) ) ;
}
2017-02-02 22:11:34 +11:00
var attemptedUser = Security . GetBackOfficeUser ( loginModel . Username ) ;
2015-07-01 17:07:29 +02:00
//create a with information to display a custom two factor send code view
2017-02-02 22:11:34 +11:00
var verifyResponse = Request . CreateResponse ( HttpStatusCode . PaymentRequired , new
2015-07-01 17:07:29 +02:00
{
2017-02-02 22:11:34 +11:00
twoFactorView = twofactorView ,
userId = attemptedUser . Id
2015-07-01 17:07:29 +02:00
} ) ;
return verifyResponse ;
case SignInStatus . LockedOut :
case SignInStatus . Failure :
default :
//return BadRequest (400), we don't want to return a 401 because that get's intercepted
// by our angular helper because it thinks that we need to re-perform the request once we are
// authorized and we don't want to return a 403 because angular will show a warning msg indicating
// that the user doesn't have access to perform this function, we just want to return a normal invalid msg.
throw new HttpResponseException ( HttpStatusCode . BadRequest ) ;
}
2013-08-12 15:06:12 +02:00
}
2016-04-12 18:07:25 +02:00
/// <summary>
/// Processes a password reset request. Looks for a match on the provided email address
/// and if found sends an email with a link to reset it
/// </summary>
/// <returns></returns>
[SetAngularAntiForgeryTokens]
public async Task < HttpResponseMessage > PostRequestPasswordReset ( RequestPasswordResetModel model )
{
// If this feature is switched off in configuration the UI will be amended to not make the request to reset password available.
// So this is just a server-side secondary check.
if ( UmbracoConfig . For . UmbracoSettings ( ) . Security . AllowPasswordReset = = false )
{
throw new HttpResponseException ( HttpStatusCode . BadRequest ) ;
}
var identityUser = await SignInManager . UserManager . FindByEmailAsync ( model . Email ) ;
if ( identityUser ! = null )
{
var user = Services . UserService . GetByEmail ( model . Email ) ;
2017-05-20 14:33:33 +02:00
if ( user ! = null )
2016-04-12 18:07:25 +02:00
{
var code = await UserManager . GeneratePasswordResetTokenAsync ( identityUser . Id ) ;
2016-09-06 08:42:53 +02:00
var callbackUrl = ConstructCallbackUrl ( identityUser . Id , code ) ;
2016-04-13 14:35:40 +02:00
var message = Services . TextService . Localize ( "resetPasswordEmailCopyFormat" ,
//Ensure the culture of the found user is used for the email!
UserExtensions . GetUserCulture ( identityUser . Culture , Services . TextService ) ,
new [ ] { identityUser . UserName , callbackUrl } ) ;
2016-04-12 18:07:25 +02:00
await UserManager . SendEmailAsync ( identityUser . Id ,
2016-04-13 14:35:40 +02:00
Services . TextService . Localize ( "login/resetPasswordEmailCopySubject" ,
//Ensure the culture of the found user is used for the email!
UserExtensions . GetUserCulture ( identityUser . Culture , Services . TextService ) ) ,
2016-04-12 18:07:25 +02:00
message ) ;
}
}
return Request . CreateResponse ( HttpStatusCode . OK ) ;
}
2017-02-02 22:11:34 +11:00
/// <summary>
/// Used to retrived the 2FA providers for code submission
/// </summary>
/// <returns></returns>
[SetAngularAntiForgeryTokens]
public async Task < IEnumerable < string > > Get2FAProviders ( )
2016-04-12 18:07:25 +02:00
{
2017-02-02 22:11:34 +11:00
var userId = await SignInManager . GetVerifiedUserIdAsync ( ) ;
if ( userId < 0 )
{
2017-02-03 00:47:28 +11:00
Logger . Warn < AuthenticationController > ( "Get2FAProviders :: No verified user found, returning 404" ) ;
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
2017-02-02 22:11:34 +11:00
}
var userFactors = await UserManager . GetValidTwoFactorProvidersAsync ( userId ) ;
return userFactors ;
}
[SetAngularAntiForgeryTokens]
public async Task < IHttpActionResult > PostSend2FACode ( [ FromBody ] string provider )
{
2017-02-03 00:47:28 +11:00
if ( provider . IsNullOrWhiteSpace ( ) )
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
var userId = await SignInManager . GetVerifiedUserIdAsync ( ) ;
if ( userId < 0 )
{
Logger . Warn < AuthenticationController > ( "Get2FAProviders :: No verified user found, returning 404" ) ;
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
2017-02-02 22:11:34 +11:00
// Generate the token and send it
if ( await SignInManager . SendTwoFactorCodeAsync ( provider ) = = false )
{
2017-02-03 00:47:28 +11:00
return BadRequest ( "Invalid code" ) ;
2017-02-02 22:11:34 +11:00
}
return Ok ( ) ;
}
[SetAngularAntiForgeryTokens]
2017-02-03 00:47:28 +11:00
public async Task < HttpResponseMessage > PostVerify2FACode ( Verify2FACodeModel model )
2017-02-02 22:11:34 +11:00
{
2017-02-03 00:47:28 +11:00
if ( ModelState . IsValid = = false )
2017-02-02 22:11:34 +11:00
{
2017-02-03 00:47:28 +11:00
return Request . CreateValidationErrorResponse ( ModelState ) ;
}
var userName = await SignInManager . GetVerifiedUserNameAsync ( ) ;
if ( userName = = null )
{
Logger . Warn < AuthenticationController > ( "Get2FAProviders :: No verified user found, returning 404" ) ;
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
var result = await SignInManager . TwoFactorSignInAsync ( model . Provider , model . Code , isPersistent : true , rememberBrowser : false ) ;
switch ( result )
{
case SignInStatus . Success :
//get the user
var user = Security . GetBackOfficeUser ( userName ) ;
return SetPrincipalAndReturnUserDetail ( user ) ;
case SignInStatus . LockedOut :
return Request . CreateValidationErrorResponse ( "User is locked out" ) ;
case SignInStatus . Failure :
default :
return Request . CreateValidationErrorResponse ( "Invalid code" ) ;
2017-02-02 22:11:34 +11:00
}
}
2017-06-27 19:55:03 +10:00
2016-04-12 18:07:25 +02:00
/// <summary>
/// Processes a set password request. Validates the request and sets a new password.
/// </summary>
/// <returns></returns>
[SetAngularAntiForgeryTokens]
public async Task < HttpResponseMessage > PostSetPassword ( SetPasswordModel model )
{
var result = await UserManager . ResetPasswordAsync ( model . UserId , model . ResetCode , model . Password ) ;
if ( result . Succeeded )
{
2017-05-20 14:33:33 +02:00
var lockedOut = await UserManager . IsLockedOutAsync ( model . UserId ) ;
if ( lockedOut )
{
Logger . Info < AuthenticationController > (
"User {0} is currently locked out, unlocking and resetting AccessFailedCount" ,
( ) = > model . UserId ) ;
2017-05-22 08:36:02 +02:00
//var user = await UserManager.FindByIdAsync(model.UserId);
var unlockResult = await UserManager . SetLockoutEndDateAsync ( model . UserId , DateTimeOffset . Now ) ;
if ( unlockResult . Succeeded = = false )
2017-05-20 14:33:33 +02:00
{
2017-05-22 08:36:02 +02:00
Logger . Warn < AuthenticationController > ( "Could not unlock for user {0} - error {1}" ,
( ) = > model . UserId , ( ) = > unlockResult . Errors . First ( ) ) ;
}
var resetAccessFailedCountResult = await UserManager . ResetAccessFailedCountAsync ( model . UserId ) ;
if ( resetAccessFailedCountResult . Succeeded = = false )
{
Logger . Warn < AuthenticationController > ( "Could not reset access failed count {0} - error {1}" ,
( ) = > model . UserId , ( ) = > unlockResult . Errors . First ( ) ) ;
2017-05-20 14:33:33 +02:00
}
}
2016-04-12 18:07:25 +02:00
return Request . CreateResponse ( HttpStatusCode . OK ) ;
}
2016-04-12 19:54:04 +02:00
return Request . CreateValidationErrorResponse (
result . Errors . Any ( ) ? result . Errors . First ( ) : "Set password failed" ) ;
2016-04-12 18:07:25 +02:00
}
2013-11-20 14:18:03 +11:00
2013-08-12 15:06:12 +02:00
/// <summary>
/// Logs the current user out
/// </summary>
/// <returns></returns>
2013-12-02 17:20:50 +11:00
[ClearAngularAntiForgeryToken]
2013-12-03 11:36:17 +11:00
[ValidateAngularAntiForgeryToken]
2013-08-12 15:06:12 +02:00
public HttpResponseMessage PostLogout ( )
2015-07-23 12:03:50 +02:00
{
2015-12-15 10:34:11 +01:00
Request . TryGetOwinContext ( ) . Result . Authentication . SignOut (
Core . Constants . Security . BackOfficeAuthenticationType ,
Core . Constants . Security . BackOfficeExternalAuthenticationType ) ;
2015-11-19 18:12:21 +01:00
2015-07-23 12:03:50 +02:00
Logger . Info < AuthenticationController > ( "User {0} from IP address {1} has logged out" ,
( ) = > User . Identity = = null ? "UNKNOWN" : User . Identity . Name ,
( ) = > TryGetOwinContext ( ) . Result . Request . RemoteIpAddress ) ;
2013-08-12 15:06:12 +02:00
return Request . CreateResponse ( HttpStatusCode . OK ) ;
}
2017-02-03 00:47:28 +11:00
/// <summary>
/// This is used when the user is auth'd successfully and we need to return an OK with user details along with setting the current Principal in the request
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
private HttpResponseMessage SetPrincipalAndReturnUserDetail ( IUser user )
{
if ( user = = null ) throw new ArgumentNullException ( "user" ) ;
var userDetail = Mapper . Map < UserDetail > ( user ) ;
//update the userDetail and set their remaining seconds
userDetail . SecondsUntilTimeout = TimeSpan . FromMinutes ( GlobalSettings . TimeOutInMinutes ) . TotalSeconds ;
//create a response with the userDetail object
var response = Request . CreateResponse ( HttpStatusCode . OK , userDetail ) ;
//ensure the user is set for the current request
Request . SetPrincipalForRequest ( user ) ;
return response ;
}
2017-02-02 22:11:34 +11:00
private string ConstructCallbackUrl ( int userId , string code )
{
// Get an mvc helper to get the url
var http = EnsureHttpContext ( ) ;
var urlHelper = new UrlHelper ( http . Request . RequestContext ) ;
var action = urlHelper . Action ( "ValidatePasswordResetCode" , "BackOffice" ,
new
{
area = GlobalSettings . UmbracoMvcArea ,
u = userId ,
r = code
} ) ;
// Construct full URL using configured application URL (which will fall back to request)
var applicationUri = new Uri ( ApplicationContext . UmbracoApplicationUrl ) ;
var callbackUri = new Uri ( applicationUri , action ) ;
return callbackUri . ToString ( ) ;
}
private HttpContextBase EnsureHttpContext ( )
{
var attempt = this . TryGetHttpContext ( ) ;
if ( attempt . Success = = false )
throw new InvalidOperationException ( "This method requires that an HttpContext be active" ) ;
return attempt . Result ;
}
2015-03-24 20:17:37 +11:00
private void AddModelErrors ( IdentityResult result , string prefix = "" )
{
foreach ( var error in result . Errors )
{
ModelState . AddModelError ( prefix , error ) ;
}
}
2013-08-12 15:06:12 +02:00
}
2013-06-17 01:06:31 +02:00
}