0

I am having problem integrating JWT with my Web API.. I tried to follow this tutorial and example

It seems pretty straight forward, but I am having difficulties integrating it with my project. You should know that I have a bunch of .aspx (Web Form) files that makes my website. This website is consuming my Web API using javascript (Ajax). I have installed jose-jwt package so I can use it in my code.

Server Side

WebApiConfig.cs:

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "defaultApiRoutes",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional },
                constraints: new { id = @"\d+" }   // Only matches if "id" is one or more digits.
            );

            config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));

        }
    }

Example of one of my actions inside 'Request' Controller:

[HttpPost]
        [ActionName("createRequest")]
        public IHttpActionResult createRequest(Request request)
        {
            if (userIsAuthorized) // I am guessing that for each action there will be this kinda condition to check the token of the user
            if (ModelState.IsValid) {
                using (SqlConnection connection = WebApiApplication.reqeustConnection("ConStrMRR")) {
                    using (SqlCommand command = new SqlCommand("createRequest", connection)) {
                        try {
                            command.CommandType = CommandType.StoredProcedure;
                            command.Parameters.Add(new SqlParameter("@status_id", request.StatusID));
                            command.Parameters.Add(new SqlParameter("@patient_firstname", request.PatientFirstName));
                            command.Parameters.Add(new SqlParameter("@patient_lastname", request.PatientLastName));
                            command.Parameters.Add(new SqlParameter("@patient_url", request.PatientURL));
                            command.Parameters.Add(new SqlParameter("@facility", request.Facility));
                            connection.Open();
                            int request_id = (int)command.ExecuteScalar();
                            return Ok(request_id);
                        } catch (Exception e) {
                            throw e;
                        } finally {
                            connection.Close();
                        }
                    }
                }
            }
            return Content(HttpStatusCode.BadRequest, "Request has not been created.");
        }

Client Side

Create-request.js

$.ajax({
            url: "http://" + window.myLocalVar + "/api/requests/createRequest",
            type: "POST",
            dataType: 'json',
            contentType: 'application/json',
            data: request,
            success: function (request_id, state) {
                    console.log(request_id);
            },
            error: function (err) {
                if (err) {
                    notyMessage(err.responseJSON, 'error');
                }
            }
        });      

I am guessing that the previous request will be updated to have the following after the 'success' function:

beforeSend: function(xhr)
              {
                xhr.setRequestHeader("Authorization", "Bearer " + localStorage.getItem('token'));
              },

My login page is like the following:

<body id="cover">

<div class="container">
    <div class="row">
        <div class="col-md-4 col-md-offset-4">
            <div class="login-panel panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title">Please Sign In</h3>
                </div>
                <div class="panel-body">
                    <div align="center" style="margin-bottom: 50px;"><img class="img-responsive" src="../img/logo.jpg"/></div>
                    <form role="form" runat="server">
                        <fieldset>
                            <div class="form-group">
                                <asp:TextBox ID="usernameTextBox" CssClass="form-control" runat="server" placeholder="Username"></asp:TextBox>
                            </div>
                            <div class="form-group">
                                <asp:TextBox ID="passwordTextBox" CssClass="form-control" runat="server" placeholder="Password" TextMode="Password"></asp:TextBox>
                            </div>
                            <div class="checkbox">
                                <label>
                                    <asp:CheckBox ID="rememberMeCheckBox" runat="server"/>Remember Me
                                </label>
                            </div>
                            <!-- Change this to a button or input when using this as a form -->
                            <asp:Button CssClass="btn btn-primary btn-block" Text="Login" ID="Login" runat="server"/>
                        </fieldset>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

I am having difficulties to integrate JWT authentication with my code. Could you please help with that.

Thanks!

Ray
  • 781
  • 2
  • 17
  • 42
  • Before diving into a lengthy response, I'd like to know the kind of scenario you are trying to solve. The big question would be "Why are you trying to use JWT authentication?". Related questions: is your web api consumed only by your own application, or are you expecting calls from third party clients? Are you using session cookies for the webform parts of your web site? JWT authentication is usually used for Single Page Applications, but if you have session cookies already in place in your regular web app, you can use the same mechanism for API calls, without worrying about JWTs. – Nico Sabena Nov 05 '16 at 21:00
  • @Nico, There will be no third parity in this. The scenario is I have built a simple API and consume it using web pages. I am using plain Jquery/Ajax to consume the API. I have an active directory on an Azure machine. What I want to know is how to authenticate users from DC and if the user is validated, then this user will have a token that s/he can use while making requests to the API.. So the first thing is to have a token in order to make a request to the API otherwise the request will get rejected. I am not using a cookie or a session. – Ray Nov 09 '16 at 16:11
  • As I mentioned before I am consuming my API through the View (Ajax) and I want to pass the token with each request and let the back end validate it. I am honestly not sure if the API should validate the token or Azure has something that is already implemented for that purpose. – Ray Nov 09 '16 at 16:12
  • Next step for me is to check whether a user can perform a request based on his/her role also retrieved form DC. So if a use is authenticated, then return a token and a role and save them (let's say in local storage or somewhere accessible throughout my website.) – Ray Nov 09 '16 at 16:12
  • So, the web API would be implemented with ASP.Net Web API, in a server/application that is completely separated from the Web Forms application that will be the client, right? – Nico Sabena Nov 11 '16 at 18:58
  • @NicoSabena right – Ray Nov 18 '16 at 18:22
  • I should mention that I have chosen a website instead of Web form application. I put a bunch of web forms inside this website. – Ray Nov 18 '16 at 18:34
  • @NicoSabena, I am thinking that if the user tried to put their credentials then, the code behind will query the Domain controller, validate the credentials, return the displayed name, job title, and a token. The displayed name I am going to put in the landing page let's call it dashboard.. the job title I am going to use it to hide/show elements on the screen and check if a user can perform a certain request .. that means that I am going somehow to export the job title to be accessible through javascript because all my requests are done through ajax/javascript. – Ray Nov 18 '16 at 18:49
  • I should also export the token so I can send it in the request body (that's my guessing). I don't know if I should have a table in the database that validate the token, I don't know if I am heading in the wrong direction (deadend) – Ray Nov 18 '16 at 18:49
  • It all is actually amazingly simple - see my answer https://stackoverflow.com/a/52822749/5374333 – Alex Herman Oct 15 '18 at 18:46

1 Answers1

1

So, you will have:

  1. One Web API server (the "API")
  2. One Web forms application (the "Client")

Web API Server

The API will be protected by JWT. Every client of the API should provide a JWT in the HTTP header (a Bearer token). This JWT will be given by the Identity Provider at the time of authentication.

The Web API needs some kind of middleware to get the JWT token from the request, validate it (verify audience, issuer, expiration and signature) and set a ClaimsPrincipal that will be valid for the request. That way you can use .Net standard authorization attributes and procedures, such as:

[Authorize] // requires the user to be authenticated
public IActionResult SomeProtectedAction()
{
}

If your Web API is for ASP.Net Core, you can use the Microsoft.AspNetCore.Authentication.JwtBearer to do that, configured like this:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    var options = new JwtBearerOptions
    {
        Audience = "[Your API ID]",
        Authority = $"[URL for your identity provider]/",
        // certificate public keys will be read automatically from
        // the identity provider if possible
        // If using symmetric keys, you will have to provide them
    };
    app.UseJwtBearerAuthentication(options);

}

Regular ASP.Net applications with OWIN can use the Microsoft.Owin.Security.ActiveDirectory package, with configuration code like this:

public void Configuration(IAppBuilder app)
{
    var issuer = $"[url to identity provider]/";
    var audience = "[your API id];

    app.UseActiveDirectoryFederationServicesBearerAuthentication(
        new ActiveDirectoryFederationServicesBearerAuthenticationOptions
        {
            TokenValidationParameters = new TokenValidationParameters
            {
                ValidAudience = audience,
                ValidIssuer = issuer
                // you will also have to configure the keys/certificates
            }
        });

Client

Your client application will be a webforms app. After the user signs in (usually by redirecting the user to the identity provider's login page), you will get an access token back. You can store the token in the client (local storage), and use it when making calls to the API as you showed:

beforeSend: function(xhr) {
    xhr.setRequestHeader("Authorization", "Bearer " + localStorage.getItem('token'));
},
Community
  • 1
  • 1
Nico Sabena
  • 6,764
  • 1
  • 14
  • 11