Search code examples
c#asp.netasp.net-identity.net-4.5

Return JWT Token generated by OAuthAuthorizatioServer from controller in Web API


Following @Taiseer Joudeh I was able to create simple POC of Web API. I'm able to create new account, then log-in and call secure Web API when I add JWT token to header.

I'd like to modify method that is responsible for creating accounts.
Right now I'm returning Create (201) code with new user object, but instead I'd like to return access token.

I've found similar question but it requires creating HttpClient and doing request to OAuthAuthorizatioServer TokenEndpointPath.

Second question I found requires generating temporary token that is returned to front-end, but then front-end must do additional request to server to get "real" token.

What I'd like to do is to return login response (access_token, token_type and expires_in) when I create user account. I want user to be authenticated when his account is created.

I'm using just Web API and JWT without any cookies.

EDIT: My temporary solution:
after creating user I'm doing this:

var validTime = new TimeSpan(0, 0, 0, 10);
var identity = await UserManager.CreateIdentityAsync(user, "JWT");
var jwtFormat = new CustomJwtFormat(ApplicationConfiguration.Issuer);
var authenticationProperties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow, ExpiresUtc = DateTimeOffset.UtcNow.Add(validTime) };
var authenticationTicket = new AuthenticationTicket(identity, authenticationProperties);
var token = jwtFormat.Protect(authenticationTicket);

var response = new
{
    access_token = token,
    token_type = "bearer",
    expires_in = validTime.TotalSeconds.ToInt()
};

return Ok(response);

where CustomJwtFormat comes from this awesome article.


Solution

  • Below is some code similar to what I'm doing in my application, which is using Asp.Net Core 1.0. Your signin and user registration will differ if you're not using Core 1.0.

    public async Task<string> CreateUser(string username, string password)
        {
            string jwt = String.Empty;
    
            if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
            {
                Response.StatusCode = (int)HttpStatusCode.BadRequest;
            }
    
            var user = await _userManager.FindByNameAsync(username);
            if (user == null) // user doesn't exist, create user
            {
                var newUser = await _userManager.CreateAsync(new ApplicationUser() { UserName = username }, password); 
                if (newUser.Succeeded) //user was successfully created, sign in user
                {
                    user = await _userManager.FindByNameAsync(username);
                    var signInResult = await _signInManager.PasswordSignInAsync(user, password, false, true);
                    if (signInResult.Succeeded) //user signed in, create a JWT
                    {
                        var tokenHandler = new JwtSecurityTokenHandler();
                        List<Claim> userClaims = new List<Claim>();
    
                        //add any claims to the userClaims collection that you want to be part of the JWT
                        //...
    
                        ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user.UserName, "TokenAuth"), userClaims);
                        DateTime expires = DateTime.Now.AddMinutes(30); //or whatever
    
                        var securityToken = tokenHandler.CreateToken(
                            issuer: _tokenOptions.Issuer,  //_tokenAuthOptions is a class that holds the issuer, audience, and RSA security key
                            audience: _tokenOptions.Audience,
                            subject: identity,
                            notBefore: DateTime.Now,
                            expires: expires,
                            signingCredentials: _tokenOptions.SigningCredentials
                            );
    
                        jwt = tokenHandler.WriteToken(securityToken);
                        Response.StatusCode = (int)HttpStatusCode.Created;
                        await _signInManager.SignOutAsync(); //sign the user out, which deletes the cookie that gets added if you are using Identity.  It's not needed as security is based on the JWT
                    }
                }
                //handle other cases...
            }
        }
    

    Basically, the user is created and then signed in automatically. I then build a JWT (add in any claims you want) and return it in the response body. On the client side (MVC and Angular JS) I get the JWT out of the response body and store it. It is then passed back to the server in the Authorization header of each subsequent request. Authorization policies for all server actions are based on the set of claims supplied by the JWT. No cookies, no state on the server.

    EDIT: I suppose you don't even need to call the signIn method in this process as you can just create the user, create a JWT, and return the JWT. When a user logs in on a future request you would use the Sign-In, create token, Sign-Out approach.