Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web API 2 identity. /Token Always return 404 error

I have some problems with adopt Web API 2 Identity. In project.

I add StartUp.cs

Like this:

using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
    public partial class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                ConfigureAuth(app);
            }
        }
}

After that I add partial class for enable Token authorization:

namespace MyNamespace
{
    public partial class Startup
    {
        public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
        public static string PublicClientId { get; private set; }
        public void ConfigureAuth(IAppBuilder app)
        {
            app.UseCookieAuthentication(new CookieAuthenticationOptions());
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            PublicClientId = "self";
            OAuthOptions = new OAuthAuthorizationServerOptions
            {
                TokenEndpointPath = new PathString("/Token"),
                Provider = new ApplicationOAuthProvider(PublicClientId),
                AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(14)
            };

            app.UseOAuthBearerTokens(OAuthOptions);
        }
    }
}

Likewise I implement User functional(like UserStore, UserManager).

I take "ExternalLogin" method from example and change.

    // 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);
        }

        User user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider,
            externalLogin.ProviderKey));

        bool hasRegistered = user != null;

        if (hasRegistered)
        {
            Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);



            ClaimsIdentity oAuthIdentity = await UserManager.CreateIdentityAsync(user, 
                                OAuthDefaults.AuthenticationType);

            ClaimsIdentity cookieIdentity = await UserManager.CreateIdentityAsync(user, 
                                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();
    }

After that I run my application and tried login to the app like this:

 var loginData = {
            grant_type: 'password',
            username: "test",
            password: "test"
        }; 

 $.ajax({
         type: 'POST',
         url: '/Token',
         data: loginData
        }).done(function (data) {
            alert(data.username);
            sessionStorage.setItem(tokenKey, data.access_token);
        }).fail(function (data) {
            alert(data);
        });

I got the 404 error. I try sent custom request to /Token via fiddler and this take the same result. Then i check that my api/Account/ExternalLogin action is available, this response 401 status code. I check references Owin, Microsoft.Owin all correct. What's the problem? Where I have problems?

UPD:

public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
 {
    private readonly string _publicClientId;

    [Dependency]
    public ICustomUserManager UserManager
    {
        get;set;
    }

    public ApplicationOAuthProvider(string publicClientId)
    {
        if (publicClientId == null)
        {
            throw new ArgumentNullException("publicClientId");
        }

        _publicClientId = publicClientId;
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        var userManager = context.OwinContext.GetUserManager<ICustomUserManager>();

        User user = await userManager.FindAsync(context.UserName, context.Password);

        if (user == null)
        {
            context.SetError("invalid_grant", "The user name or password is incorrect.");
            return;
        }

        ClaimsIdentity oAuthIdentity = await UserManager.CreateIdentityAsync(user, 
                                OAuthDefaults.AuthenticationType);

        ClaimsIdentity cookieIdentity = await UserManager.CreateIdentityAsync(user, 
                                CookieAuthenticationDefaults.AuthenticationType);

        AuthenticationProperties properties = CreateProperties(user.UserName);
        AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);

        var val = context.Validated(ticket);
        context.Request.Context.Authentication.SignIn(cookieIdentity);
    }

    public override Task TokenEndpoint(OAuthTokenEndpointContext context)
    {
        foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
        {
            context.AdditionalResponseParameters.Add(property.Key, property.Value);
        }

        return Task.FromResult<object>(null);
    }

    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        // Resource owner password credentials does not provide a client ID.
        if (context.ClientId == null)
        {
            context.Validated();
        }

        return Task.FromResult<object>(null);
    }

    public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
    {
        if (context.ClientId == _publicClientId)
        {
            Uri expectedRootUri = new Uri(context.Request.Uri, "/");

            if (expectedRootUri.AbsoluteUri == context.RedirectUri)
            {
                context.Validated();
            }
        }

        return Task.FromResult<object>(null);
    }

    public static AuthenticationProperties CreateProperties(string userName)
    {
        IDictionary<string, string> data = new Dictionary<string, string>
        {
            { "userName", userName }
        };
        return new AuthenticationProperties(data);
    }
}
like image 893
CMaker Avatar asked May 26 '15 16:05

CMaker


People also ask

How to handle 404 error in Web API c#?

A simple solution is to check for the HTTP status code 404 in the response. If found, you can redirect the control to a page that exists. The following code snippet illustrates how you can write the necessary code in the Configure method of the Startup class to redirect to the home page if a 404 error has occurred.

What is 404 error in Web API?

HTTP 404. The resource you are looking for, or one of its dependencies, could have been removed, had its name changed, or is temporarily unavailable.

What is OAuth2.0 authentication in Web API?

OAuth2 is the preferred method of authenticating access to the API. OAuth2 allows authorization without the external application getting the user's email address or password. Instead, the external application gets a token that authorizes access to the user's account.

What is token in Web API?

Token-based authentication is a process where the user sends his credential to the server, server will validate the user details and generate a token which is sent as response to the users, and user store the token in client side, so client do further HTTP call using this token which can be added to the header and ...


1 Answers

I had the same problem in my production environment but not locally (IIS Express). Nothing helps with web.config. The solution was to add explicit NuGet Package reference to: Microsoft.Owin.Host.SystemWeb

like image 88
Geoffroy Mercier Avatar answered Sep 22 '22 08:09

Geoffroy Mercier