How to authenticate from Web API controller endpoi

2019-08-08 08:36发布

问题:

I'm unable to authenticate from within my controller method, as part of my confirm-user-signup workflow and I'm looking for some guidance that my implementation is correct.

My controller method has the following code; I've confirmed that the user is populated (as part of the FindById call) but afterSignIn; this.Authentication.User.Identity is not set (Name is blank and IsAuthenticated is false):

this.Authentication.SignOut("JWT");

ApplicationUser user = await this.AppUserManager.FindByIdAsync(userId);

ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(this.AppUserManager, "JWT");

this.Authentication.SignIn(new AuthenticationProperties() { IsPersistent = true }, oAuthIdentity);

Is this correct way to automate signin when using OWIN OAuth?

The full controller method can be see here: https://gist.github.com/chrismoutray/8a8e5f6a7b433571613b

For reference, I've been following a set of articles from a blog called Bit of Tech which has allowed me to set OWIN OAuth using JWT bearer token.

Part 3 in particular talks about JWT (of 5) : http://bitoftech.net/2015/02/16/implement-oauth-json-web-tokens-authentication-in-asp-net-web-api-and-identity-2/

From the articles I've created custom OAuth provider that inherits from OAuthAuthorizationServerProvider and implements the GrantResourceOwnerCredentials, and when my client code (AngularJS) tries to authenticate using the endpoint /oauth/token, I can see that it gives the correct response and the secured endpoints (using Authorise attribute) can then be accessed.

So authentication via the middleware does work but what is the correct way to authenticate from inside a controller method?

回答1:

For what it's worth I thought I’d share the solution I’ve come up with.

The short answer; I create a temporary one time password (token) which will be used to authenticate users for the first time

Gist here for reference: https://gist.github.com/chrismoutray/159e6fd74f45d88efd12

To Summaries – In the AccountController ConfirmSignUp method; I use the user-manager to generate a custom token which I’ve called GRANT-ACCESS, then redirect to my confirm-signup page with the username and token in the uri. My angular app resolves the ui-route to confirm-signup and performs a login, passing the token as the password.

var tokenResult = this.AppUserManager.GenerateUserTokenAsync("GRANT-ACCESS", userId);
string token = tokenResult.Result;

Uri redirectLocation = new Uri(
    string.Format("http://localhost:45258/#/confirm-signup?user={0}&token={1}", 
    Uri.EscapeDataString(user.UserName), Uri.EscapeDataString(token)));

return Redirect(redirectLocation);

Finally there is an amendment to GrantResourceOwnerCredentials, so that if the FindAsync (by username and password) doesn’t return the user then I try again but this time treating the context.Password as the GRANT-ACCESS user token to verify. If the token is valid then I return the JWT authentication ticket as if the user had logged in with a valid password.

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
    ... code obmitted

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

    if (user == null)
    {
        user = await userManager.FindByNameAsync(context.UserName);

        ... null checks obmitted

        string token = context.Password;
        bool result = await userManager.VerifyUserTokenAsync(user.Id, "GRANT-ACCESS", token);
    }

    ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");

    var ticket = new AuthenticationTicket(oAuthIdentity, null);

    context.Validated(ticket);
}