使用Asp.Net Web API对预检的响应无效(重定向)

时间:2017-06-11 10:33:29

标签: asp.net-web-api

我正在开发一个网站,它在前端使用AngularJS,在后端使用ASP.NET Web API。我正在尝试为我的网站配置外部登录(Facebook)。我已经在API上启用了Cors。 API调用的顺序是:

  1. API调用 api/Account/ExternalLogins?returnUrl=%2F&generateState=true 获取外部登录提供商列表。
  2. 返回
    [{"name":"Facebook", "url":"/api/Account/ExternalLogin?provider=Facebook&response_type=token&client_id=self&redirect_uri=http%3A%2F%2Flocalhost%3A15359%2F&state=xxxxxxxxxx", "state":"xxxxxxxxxx"}]
  3. 现在我发送一个GET请求给之前返回的url。这会触发对https://www.facebook.com/dialog/oauth?response_type=code&client_id=xxxxxxx&redirect_uri=https%3A%2F%2Flocalhost%3A44327%2Fsignin-facebook&scope=manage_pages&state=xxxxxxx的预检请求,从而产生错误。这永远不会重定向到Facebook登录页面。
  4. /api/Account/ExternalLogin?provider=Facebook&response_type=token&client_id=self&redirect_uri=http%3A%2F%2Flocalhost%3A15359%2F&state=xxxxxxxxxx的请求和响应标头都包含Access-Control-Allow-Origin:*

    但是对于预检请求https://www.facebook.com/dialog/oauth?response_type=code&client_id=xxxxxxx&redirect_uri=https%3A%2F%2Flocalhost%3A44327%2Fsignin-facebook&scope=manage_pages&state=xxxxxxx缺少Access-Control-Allow-Origin标头。

    下面提到了代码段。

    app.js

        $httpProvider.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
        $httpProvider.defaults.headers.common['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, Content-Length, X-Requested-With';
        $httpProvider.defaults.headers.common['Access-Control-Allow-Credentials'] = true;
        $httpProvider.defaults.headers.common['Access-Control-Allow-Method'] = 'GET, PUT, POST, DELETE, OPTIONS';
    

    Start.Auth.cs

           // Configure the db context, user manager and signin manager to use a single instance per request
            app.CreatePerOwinContext(ApplicationDbContext.Create);
            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
            app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
    
            app.UseCors(CorsOptions.AllowAll);
    
            // Enable the application to use a cookie to store information for the signed in user
            // and to use a cookie to temporarily store information about a user logging in with a third party login provider
            // Configure the sign in cookie
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/api/Account/ExternalLogin"),
                Provider = new CookieAuthenticationProvider
                {
                    // Enables the application to validate the security stamp when the user logs in.
                    // This is a security feature which is used when you change a password or add an external login to your account.  
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
                }
            });
    
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
    
            // Configure the application for OAuth based flow
            PublicClientId = "self";
            OAuthOptions = new OAuthAuthorizationServerOptions
            {
                TokenEndpointPath = new PathString("/Token"),
                Provider = new ApplicationOAuthProvider(PublicClientId),
                AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
    
                // In production mode set AllowInsecureHttp = false
                AllowInsecureHttp = true
                //Provider = new AuthorizationServerProvider()
            };
    
            // Enable the application to use bearer tokens to authenticate users
            app.UseOAuthBearerTokens(OAuthOptions);
    
            // Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
            app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
    
            // Enables the application to remember the second login verification factor such as phone or email.
            // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
            // This is similar to the RememberMe option when you log in.
            app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
    
    
    
            var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions
            {
                AppId = "xxxxxxxxx",
                AppSecret = "xxxxxxxxx",
                BackchannelHttpHandler = new FacebookBackChannelHandler(),
    
                Provider = new FacebookAuthenticationProvider()
                {
                    OnAuthenticated = (context) =>
                    {
                        context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
                        return Task.FromResult(0);
                    }
    
                },
                SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
                SendAppSecretProof = true
            };
    
            facebookOptions.Scope.Add(ConfigurationManager.AppSettings["Facebook_Scope"]);
            facebookOptions.AuthenticationMode = AuthenticationMode.Passive;
            app.UseFacebookAuthentication(facebookOptions);
    

    的AccountController

    [Authorize]
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/Account")]
    public class AccountController : ApiController
    {
        private const string LocalLoginProvider = "Local";
        private ApplicationUserManager _userManager;
    
        public AccountController()
        {
        }
    
        public AccountController(ApplicationUserManager userManager,
            ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
        {
            UserManager = userManager;
            AccessTokenFormat = accessTokenFormat;
        }
    
        public ApplicationUserManager UserManager
        {
            get
            {
                return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
            }
            private set
            {
                _userManager = value;
            }
        }
    
        public ISecureDataFormat<AuthenticationTicket> AccessTokenFormat { get; private set; }
    
        // GET api/Account/UserInfo
        [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
        [Route("UserInfo")]
        public UserInfoViewModel GetUserInfo()
        {
            ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
    
            return new UserInfoViewModel
            {
                Email = User.Identity.GetUserName(),
                HasRegistered = externalLogin == null,
                LoginProvider = externalLogin != null ? externalLogin.LoginProvider : null
            };
        }
    
        // POST api/Account/Logout
        [Route("Logout")]
        [AllowAnonymous]
        public IHttpActionResult Logout()
        {
            Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
            return Ok();
        }
    
    
        // POST api/Account/AddExternalLogin
        [Route("AddExternalLogin")]
        public async Task<IHttpActionResult> AddExternalLogin(AddExternalLoginBindingModel model)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
    
            Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
    
            AuthenticationTicket ticket = AccessTokenFormat.Unprotect(model.ExternalAccessToken);
    
            if (ticket == null || ticket.Identity == null || (ticket.Properties != null
                && ticket.Properties.ExpiresUtc.HasValue
                && ticket.Properties.ExpiresUtc.Value < DateTimeOffset.UtcNow))
            {
                return BadRequest("External login failure.");
            }
    
            ExternalLoginData externalData = ExternalLoginData.FromIdentity(ticket.Identity);
    
            if (externalData == null)
            {
                return BadRequest("The external login is already associated with an account.");
            }
    
            IdentityResult result = await UserManager.AddLoginAsync(User.Identity.GetUserId(),
                new UserLoginInfo(externalData.LoginProvider, externalData.ProviderKey));
    
            if (!result.Succeeded)
            {
                return GetErrorResult(result);
            }
    
            return Ok();
        }
    
        // GET api/Account/ExternalLogin
        [OverrideAuthentication]
        [HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
        [AllowAnonymous]
        [Route("ExternalLogin", Name = "ExternalLogin")]
        public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null)
        {
            if (error != null)
            {
                return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error));
            }
    
            if (!User.Identity.IsAuthenticated)
            {
                return new ChallengeResult(provider, this);
            }
    
            ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
    
            if (externalLogin == null)
            {
                return InternalServerError();
            }
    
            if (externalLogin.LoginProvider != provider)
            {
                Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
                return new ChallengeResult(provider, this);
            }
    
            ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider,
                externalLogin.ProviderKey));
    
            bool hasRegistered = user != null;
    
            if (hasRegistered)
            {
                Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
    
                ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
                   OAuthDefaults.AuthenticationType);
                ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
                    CookieAuthenticationDefaults.AuthenticationType);
    
                AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
                Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
            }
            else
            {
                IEnumerable<Claim> claims = externalLogin.GetClaims();
                ClaimsIdentity identity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
                Authentication.SignIn(identity);
            }
    
            return Ok();
        }
    
        // GET api/Account/ExternalLogins?returnUrl=%2F&generateState=true
        [AllowAnonymous]
        [Route("ExternalLogins")]
        public IEnumerable<ExternalLoginViewModel> GetExternalLogins(string returnUrl, bool generateState = false)
        {
            IEnumerable<AuthenticationDescription> descriptions = Authentication.GetExternalAuthenticationTypes();
            List<ExternalLoginViewModel> logins = new List<ExternalLoginViewModel>();
    
            string state;
    
            if (generateState)
            {
                const int strengthInBits = 256;
                state = RandomOAuthStateGenerator.Generate(strengthInBits);
            }
            else
            {
                state = null;
            }
    
            foreach (AuthenticationDescription description in descriptions)
            {
                ExternalLoginViewModel login = new ExternalLoginViewModel
                {
                    Name = description.Caption,
                    Url = Url.Route("ExternalLogin", new
                    {
                        provider = description.AuthenticationType,
                        response_type = "token",
                        client_id = Startup.PublicClientId,
                        redirect_uri = new Uri(Request.RequestUri, returnUrl).AbsoluteUri,
                        state = state
                    }),
                    State = state
                };
                logins.Add(login);
            }
    
            return logins;
        }
    
        // POST api/Account/Register
        [AllowAnonymous]
        [Route("Register")]
        public async Task<IHttpActionResult> Register(RegisterBindingModel model)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
    
            var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
    
            IdentityResult result = await UserManager.CreateAsync(user, model.Password);
    
            if (!result.Succeeded)
            {
                return GetErrorResult(result);
            }
    
            return Ok();
        }
    
        // POST api/Account/RegisterExternal
        [OverrideAuthentication]
        [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
        [Route("RegisterExternal")]
        public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
    
            var info = await Authentication.GetExternalLoginInfoAsync();
            if (info == null)
            {
                return InternalServerError();
            }
    
            var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
    
            IdentityResult result = await UserManager.CreateAsync(user);
            if (!result.Succeeded)
            {
                return GetErrorResult(result);
            }
    
            result = await UserManager.AddLoginAsync(user.Id, info.Login);
            if (!result.Succeeded)
            {
                return GetErrorResult(result);
            }
            return Ok();
        }
    
        protected override void Dispose(bool disposing)
        {
            if (disposing && _userManager != null)
            {
                _userManager.Dispose();
                _userManager = null;
            }
    
            base.Dispose(disposing);
        }
    
    }
    

    我知道使用MVC帐户控制器可以实现这一点。我正在寻找一种方法来完全使用web api,而不依赖于MVC。

    谢谢!

0 个答案:

没有答案