Esempio n. 1
0
        public void SignInWithLinkedAccount(
            string tenant,
            string providerName,
            string providerAccountID,
            IEnumerable <Claim> claims,
            out TAccount account)
        {
            account = null;

            if (!UserAccountService.Configuration.MultiTenant)
            {
                tenant = UserAccountService.Configuration.DefaultTenant;
            }

            if (String.IsNullOrWhiteSpace(tenant))
            {
                throw new ArgumentException("tenant");
            }
            if (String.IsNullOrWhiteSpace(providerName))
            {
                throw new ArgumentException("providerName");
            }
            if (String.IsNullOrWhiteSpace(providerAccountID))
            {
                throw new ArgumentException("providerAccountID");
            }
            if (claims == null)
            {
                throw new ArgumentNullException("claims");
            }

            Tracing.Information("[AuthenticationService.SignInWithLinkedAccount] tenant: {0}, provider: {1}, id: {2}", tenant, providerName, providerAccountID);

            var user = GetCurentPrincipal();

            if (user != null && user.Identity.IsAuthenticated)
            {
                // already logged in, so use the current user's account
                Tracing.Verbose("[AuthenticationService.SignInWithLinkedAccount] user already logged in as: {0}", user.Identity.Name);
                account = this.UserAccountService.GetByID(user.GetUserID());
            }
            else
            {
                // see if there's already an account mapped to this provider
                account = this.UserAccountService.GetByLinkedAccount(tenant, providerName, providerAccountID);
                if (account == null)
                {
                    Tracing.Verbose("[AuthenticationService.SignInWithLinkedAccount] linked account not found");

                    // no account associated, so create one
                    // we need email
                    var email = claims.GetValue(ClaimTypes.Email);
                    if (String.IsNullOrWhiteSpace(email))
                    {
                        throw new ValidationException(UserAccountService.GetValidationMessage(MembershipRebootConstants.ValidationMessages.AccountCreateFailNoEmailFromIdp));
                    }

                    // check to see if email already exists
                    if (this.UserAccountService.EmailExists(tenant, email))
                    {
                        throw new ValidationException(UserAccountService.GetValidationMessage(MembershipRebootConstants.ValidationMessages.LoginFailEmailAlreadyAssociated));
                    }

                    // guess at a username to use
                    var name = claims.GetValue(ClaimTypes.Name);
                    // remove whitespace
                    if (name != null)
                    {
                        name = ParseValidUsername(name);
                    }

                    // check to see if username already exists
                    if (String.IsNullOrWhiteSpace(name) || this.UserAccountService.UsernameExists(tenant, name))
                    {
                        // try use email for name then
                        name = email.Substring(0, email.IndexOf('@'));
                        name = ParseValidUsername(name);

                        if (this.UserAccountService.UsernameExists(tenant, name))
                        {
                            // gen random username -- this isn't ideal but
                            // they should always be able to change it later
                            name = Guid.NewGuid().ToString("N");
                        }
                    }

                    // create account without password -- user can verify their email and then
                    // do a password reset to assign password
                    Tracing.Verbose("[AuthenticationService.SignInWithLinkedAccount] creating account: {0}, {1}", name, email);
                    account = this.UserAccountService.CreateAccount(tenant, name, null, email);

                    // update account with external claims
                    var cmd = new MapClaimsToAccount <TAccount> {
                        Account = account, Claims = claims
                    };
                    this.UserAccountService.ExecuteCommand(cmd);
                    this.UserAccountService.Update(account);
                }
                else
                {
                    Tracing.Verbose("[AuthenticationService.SignInWithLinkedAccount] linked account found: {0}", account.ID);
                }
            }

            if (account == null)
            {
                throw new Exception("Failed to locate account");
            }

            // add/update the provider with this account
            this.UserAccountService.AddOrUpdateLinkedAccount(account, providerName, providerAccountID, claims);

            // log them in if the account if they're verified
            if (account.IsAccountVerified || !UserAccountService.Configuration.RequireAccountVerification)
            {
                Tracing.Verbose("[AuthenticationService.SignInWithLinkedAccount] signing user in: {0}", account.ID);
                // signin from the account
                // if we want to include the provider's claims, then perhaps this
                // should be done in the claims transformer
                this.SignIn(account, providerName);
            }
            else
            {
                Tracing.Error("[AuthenticationService.SignInWithLinkedAccount] user account not verified, not allowed to login: {0}", account.ID);
            }
        }
        public void SignInWithLinkedAccount(
            string tenant,
            string providerName,
            string providerAccountID,
            IEnumerable <Claim> claims,
            out TAccount account)
        {
            account = null;

            if (!UserAccountService.Configuration.MultiTenant)
            {
                tenant = UserAccountService.Configuration.DefaultTenant;
            }

            if (String.IsNullOrWhiteSpace(tenant))
            {
                throw new ArgumentException("tenant");
            }
            if (String.IsNullOrWhiteSpace(providerName))
            {
                throw new ArgumentException("providerName");
            }
            if (String.IsNullOrWhiteSpace(providerAccountID))
            {
                throw new ArgumentException("providerAccountID");
            }
            if (claims == null)
            {
                throw new ArgumentNullException("claims");
            }

            var user = GetCurentPrincipal();

            if (user != null && user.Identity.IsAuthenticated)
            {
                // already logged in, so use the current user's account
                account = this.UserAccountService.GetByID(user.GetUserID());
            }
            else
            {
                // see if there's already an account mapped to this provider
                account = this.UserAccountService.GetByLinkedAccount(tenant, providerName, providerAccountID);
                if (account == null)
                {
                    // no account associated, so create one
                    // we need email
                    var email = claims.GetValue(ClaimTypes.Email);
                    if (String.IsNullOrWhiteSpace(email))
                    {
                        throw new ValidationException(UserAccountService.GetValidationMessage(MembershipRebootConstants.ValidationMessages.AccountCreateFailNoEmailFromIdp));
                    }

                    // guess at a name to use
                    var name = claims.GetValue(ClaimTypes.Name);
                    if (name == null ||
                        this.UserAccountService.UsernameExists(tenant, name))
                    {
                        name = email;
                    }
                    else
                    {
                        name = name.Replace(" ", "");
                    }

                    // check to see if email already exists
                    if (this.UserAccountService.EmailExists(tenant, email))
                    {
                        throw new ValidationException(UserAccountService.GetValidationMessage(MembershipRebootConstants.ValidationMessages.LoginFailEmailAlreadyAssociated));
                    }

                    // auto-gen a password, they can always reset it later if they want to use the password feature
                    // this is slightly dangerous if we don't do email account verification, so if email account
                    // verification is disabled then we need to be very confident that the external provider has
                    // provided us with a verified email
                    account = this.UserAccountService.CreateAccount(tenant, name, null, email);

                    // update account with external claims
                    var cmd = new MapClaimsToAccount <TAccount> {
                        Account = account, Claims = claims
                    };
                    this.UserAccountService.ExecuteCommand(cmd);
                    this.UserAccountService.Update(account);
                }
            }

            if (account == null)
            {
                throw new Exception("Failed to locate account");
            }

            // add/update the provider with this account
            this.UserAccountService.AddOrUpdateLinkedAccount(account, providerName, providerAccountID, claims);

            // log them in if the account if they're verified
            if (account.IsAccountVerified || !UserAccountService.Configuration.RequireAccountVerification)
            {
                // signin from the account
                // if we want to include the provider's claims, then perhaps this
                // should be done in the claims transformer
                this.SignIn(account, providerName);
            }
        }