6

I don't know if I'm just not looking in the right places, but I cannot seem to find the right guidance on where to begin working with React / .NET Core 2.1 Web API and (on-prem) Active Directory authentication.

I'm relatively new to .NET authentication in general, and completely new to Active Directory authentication.

I started by using the .NET Core 2.1 React template and attempting to add auth to it, but got completely lost.

Where do I even start?

1 Answer 1

15

For me, step one was to set up JWT authentication, such as described in this MSDN blog post.

Next, I had to find a library to use to check a user against Active Directory. I chose System.DirectoryServices.AccountManagement (available for .NET Core).

Now, I had to create a new controller with an [AllowAnonymous]attribute. I called it LoginController, and created an action that looked like the following:

    [AllowAnonymous]
    [HttpPost]
    // Notice: We get a custom request object from the body
    public async Task<IActionResult> Login([FromBody] AuthRequest request)
    {
            // Create a context that will allow you to connect to your Domain Controller
            using (var adContext = new PrincipalContext(ContextType.Domain, "mydomain.com"))
            {
                    var result = adContext.ValidateCredentials(request.username, request.password);
                    if (result)
                    {
                        // Create a list of claims that we will add to the token. 
                        // This is how you can control authorization.
                        var claims = new[]
                        {
                            // Get the user's Name (this can be whatever claims you wish)
                            new Claim(ClaimTypes.Name, request.username)
                        };

                        // Read our custom key string into a a usable key object 
                        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration.GetSection("SOME_TOKEN").Value));
                        // create some signing credentials using out key
                        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

                        // create a JWT 
                        var token = new JwtSecurityToken(
                            issuer: "mydomain.com",
                            audience: "mydomain.com",
                            claims: claims, // the claims listed above
                            expires: DateTime.Now.AddMinutes(30), // how long you wish the token to be active for
                            signingCredentials: creds);

                        Since we return an IActionResult, wrap the token inside of a status code 200 (OK)
                        return Ok(new
                        {
                            token = new JwtSecurityTokenHandler().WriteToken(token)
                        });
                    }
                }
            }
        }
        // if we haven't returned by now, something went wrong and the user is not authorized
        return Unauthorized();
    }

The AuthRequest object could look something like this:

    public class AuthRequest
    {
        public string username { get; set; }
        public string password { get; set; }
    }

Now, in my React app, all I have to do is make a simple fetch request to the LoginController with the user's username & password that I can get from a login form. The result will be a JWT I can save to state (But should save to cookies: the react-cookie library makes that trivial).

        fetch(`login`, {
            method: "POST",
            headers: {
                'content-type': 'application/json',
                'accept': 'application/json',
            },
            body: JSON.stringify({this.state.username, this.state.password})
        }).then((response) => {
            if (response.status === 401) {
                // handle the 401 gracefully if this user is not authorized
            }
            else {
                // we got a 200 and a valid token
                response.json().then(({ token }) => {
                    // handle saving the token to state/a cookie
                })
            }
        })

You now have the ability to add the [Authorize] attribute to any of your controllers in your .NET Core application, and make a fetch request to it while passing your JWT from your React client, like this:

await fetch(`someController/someAction`, 
  {  
      method: 'GET'
      headers: {
          'content-type': 'application/json',
          'authorization': `Bearer ${YOUR_JWT}`
      }
  })
  .then(response => doSomething());

If you wanted to use this JWT with a SignalR Hub, add the [Authorize] attribute to your Hub in your .NET Core project. Then, In your React client, when you instantiate the connection to your hub:

import * as signalR from '@aspnet/signalr';

var connection = new signalR.HubConnectionBuilder().withUrl('myHub', { accessTokenFactory: () => YOUR_JWT })

And, viola! A .NET Core React application capable of authorized real-time communication!

Sign up to request clarification or add additional context in comments.

5 Comments

Perfect answer. Valid still for 2022 and NET6 (with slight change on server side - instead of app.UseJwtBearerAuthentication, NET6 uses .AddJwtBearer. Thank you.
So where is the JWT token being stored in the WebApi server app so it can be retrieved and re-used to validate future requests from the client web app? It says it saves it in 'state'. What is 'state' for this? It also mentions 'cookies' which are browser/client side, and not in a WebApi server app. Or is this code all rolled into the web client? If so, its not answering the question is it?
@MarkWorrall Either I'm misunderstanding your question or you're misunderstanding how JWTs work. You don't keep a copy of the token on the server. Your server has access to the secret key used to sign the JWT. With each request, the signature of the JWT in the header is validated against this secret key.
@MarkWorrall Regarding "state" this is referring to saving the JWT on the client using useState etc. Of course, you should actually be saving this to cookies. Even better, the login method should return a set-cookie header with the proper domain, then on the client side you will send this cookie with every request.
@MarkWorrall the title of the question being "Active Directory Authentication with .NET Core Web API and React", my answer is specific to React, but is generally applicable for all JS clients once you look past React-isms. With hindsight I would have added set-cookie in the login response and emphasized security more. Regardless, I can assure you that my answer to my own question indeed does answer it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.