12.5 Lab - Securing API With JWT

Overview

12.5 Lab - Securing API With JWT

In this lab we will be updating the Netflix Titles application from the 12.4 lab. We will be adding the ability to delete a title from the database using an HttpDelete request. We will also learn how to secure our API with a JSON Web Token.

Work through the stories in order. Use the acceptance criteria to check each feature, and complete the nested tasks using the specified names, values, and scenarios.

Checkpoint reference
Operation Checkpoint response
GET 200 OK with the requested data.
POST 200 OK with the created result, as required here.
PUT (12.4 onward) 200 OK after the update.
DELETE (12.5) 204 No Content after deletion.
Delete a record
Acceptance Criteria
Acceptance criteria
  • The DELETE endpoint removes the requested record.
  • A successful deletion returns HTTP 204 with no response body.
  • A subsequent query confirms the record is absent.
Instructions
1.Add the ability to delete a title.

In most cases, we would want to archive a record in a database instead of completely removing it, however, to utilize an HttpDelete request, we will be removing a Netflix title from the database instead of archiving it.

A In the dataService class, add a method named DeleteTitle that returns void and takes a parameter of type Title named title.
1 Call the _dataContext's field Titles properties Remove method, passing in the title parameter.
2 Call the _dataContext's field SaveChanges method.
B In the APIController, create a method named DeleteTitle that returns an IActionResult and accepts a parameter of type string named id.
1 Add an HttpDelete attribute to the method with a route of delete/title.
2 Call the _dataService's GetTitles method and set it to a List of Title named titles.
3 Find the title in the list of titles whose id matches the id parameter, set it to a Title variable named title.

Use Int32.Parse() to parse the id parameter from type string to type int.

4 Call the _dataService's DeleteTitle method, passing in the title variable.
5 Return a new StatusCodeResult passing in the status code of 204.

The 204 status code indicates that the request was successful and that there is no additional content to send in the request body.

C Check your work in Postman.
1 Start your application.
2 In Postman, create a DELETE request named DELETE/title.
3 In the URL of the request add the below url.
{{domain}}/api/delete/title
4 Add a query parameter with the key name of id and the value of 2.
DELETE Request for 12.5 Lab - Securing API With JWT. Use the adjacent task instructions to identify the required controls, output, or debugger values.
DELETE Request
5 Click send. The response should be empty with a status of 204.
6 Send the GET/titles request, ensure that there is no title with an id of 2.
Issue and validate JSON Web Tokens
Acceptance Criteria
Acceptance criteria
  • AppSettings contains JwtSigningKey and Users; User contains Username and Password.
  • The authentication service validates the configured signing key and token settings.
  • The anonymous token endpoint validates the supplied test credentials before issuing a token.
Instructions
2.Add the User class and AppSettings class to the application.
App Settings Classes; Members and relationships are shown in the editable reference; unrelated members may be omitted.
App Settings Classes
  • New
  • Changed
  • Removed
A Add the User class and AppSettings class to the application.
3.Configure a JWT based authentication service.
A In the appsettings.json file add the below code, replacing the signing key with your own values.
"AppSettings": {
"JwtSigningKey": "bPeShVmYq3t6w9z$C&F)H@McQfTjWnZr",
"Users": [
{
"Username": "admin",
"Password": "tomatoes"
}
]
}

You can use the below link to generate a random 256-bit key.

Encryption Key Generator

Encryption Key Generator
B Register a JWT authentication schema.
1 Install the Microsoft.AspNetCore.Authentication and Microsoft.AspNetCore.Authentication.JwtBearer packages.
2 In Startup.cs ConfigureServices method

Configure the appsettings.json file.

// Configures the appsettings config file.
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);

Add authentication settings to the JWT signing key.

// Adds authentication settings to the JWT signing key.
var appSettings = appSettingsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.JwtSigningKey);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
3 Map the appsettings.json file to the AppSettings class.

Install the AutoMapper.Extensions.Microsoft.DependencyInjection package.

Add the below code after adding authentication to the JWT signing key in the ConfigureServices method, to map the appsettings.json file to the AppSettings class.

// Maps the appsettings and start up to the application to allow the use of the appsettings class.
services.AddAutoMapper(typeof(Startup));
services.AddScoped<AppSettings>(serviceProvider => appSettings);
C In the Configure method, in the Startup class, add the below code after the app.UseAuthorization() call, and before the…

In the Configure method, in the Startup class, add the below code after the app.UseAuthorization() call, and before the app.UseEndpoints() call.

app.UseAuthentication();
4.Generate a JSON Web Token
A Add a new controller named AuthenticationController to the Controllers folder. Have it implement the ContollerBase class.

The Controller class is derived from the ControllerBase class and supports views. It is used for handling web pages and not API requests, or web pages and API requests. Because we will not be using the AuthenticationController to support a view or handle a web page, we will have it implement the ControllerBase class.

Add a using for Microsoft.IdentityModel.Tokens

1 Add the following attributes to the AuthenticationController at the class level.
[Route("api/[controller]")]
[ApiController, AllowAnonymous]

The ApiController attribute enables certain API specific behaviors, such as automatic HTTP 400 responses and attribute routing requirements.

The AllowAnonymous attribute allows access to non-authenticated users.

2 Create a private readonly field of type AppSettings named _appSettings.
3 Create a private readonly field of type list of Users named _users.
4 Create an AuthenticationController constructor that takes a parameter of type AppSettings named appSettings.

Set the appSettings parameter to the _appSettings field.

Set the appSettings parameter's Users property to the _users field.

5 Create a method named GetToken that returns type IActionResult and accepts two parameters, both of type string, one named…

Create a method named GetToken that returns type IActionResult and accepts two parameters, both of type string, one named username and the other named password.

Add a HttpPost attribute, with a route of "token" at the method level.

Get the user from the AuthenticationController's list of users whose username and password properties matches the passed in parameters.

User authenticateUser = _users.FirstOrDefault(user => user.Password == password && user.Username.ToLower() == username.ToLower());

Authenticate the found user by verifying that they exist. If the found user is equal to null, return a call to Unauthorized.

if (authenticateUser == null)
{
// If they do not exist return unauthorized access.
return Unauthorized();
}

The Unauthorzied method is a built in method from the ApiController class that the ApiController attribute provides access to, and creates a 401 unauthorized result.

6 If user authentication was successful, generate the JWT Token using the signing key and user credentials.

Create a new instance of the JWTSecurityTokenHandler class which will be used for creating and validating a JWT token.

var tokenHandler = new JwtSecurityTokenHandler();

Add the below code after creating a new token handler to encode the JwtSigningKey into a series of bytes, storing the result in a key variable.

var key = Encoding.ASCII.GetBytes(_appSettings.JwtSigningKey);

Create a new SecurityTokenDescriptor to act as a placeholder for all attributes of the JWT.

var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, username)
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};

A claim holds the facts about the user that holds the token, in this instance the user would be "claiming" to be an admin.

The Expires property sets the length of time that the token is valid for, in this instance the token will expire in 7 days.

The SigningCredentials class is used to define the security key and the algorithm used to generate the digital signature.

To create the token that will be generated for the user, call the tokenHandler's CreateToken method, passing in the tokenDescriptor variable. Set the result to variable named token.

var token = tokenHandler.CreateToken(tokenDescriptor);

Return the generated token.

return Content(tokenHandler.WriteToken(token));
Protect and verify the API endpoints
Acceptance Criteria
Acceptance criteria
  • Protected controllers use Authorize while the token endpoint remains accessible for authentication.
  • Postman can obtain a token with the configured test credentials and use Bearer authorization for the protected calls.
  • A missing or invalid token cannot access protected data.
Instructions
5.Only allow users that have a JWT to invoke API calls.
A Add the Authorize attribute to the APIController class
[ApiController, Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

The above Authorize attribute secures the APIController with JWT Bearer Authentication, restricting access to only users that hold a valid JWT token.

6.Check your work in Postman.
A Start your application.
B Send a the GET/titles request. It should return a 401 unauthorized response.
C Create a global variable named username with the value of admin.
D Create a global variable named password with the value of tomatoes.
E Create a new POST request with the name POST/token.
1 In the requests URL paste in the following code that points to the route of the GetToken call.
{{domain}}/authentication/token
2 Add a query parameter with the key name of username and the value of the username global variable.
3 Add a query parameter with the key name of password and the value of the password global variable.

The POST/token call should look like the below image.

Postman call for 12.5 Lab - Securing API With JWT. Use the adjacent task instructions to identify the required controls, output, or debugger values.
Postman call
F Start your application again, if previously stopped.
G Send the POST/token request.
H You should receive a response similar to the one below.
Generated JWT for 12.5 Lab - Securing API With JWT. Use the adjacent task instructions to identify the required controls, output, or debugger values.
Generated JWT

If you would like, you can look at your JWT token decoded by pasting it into the Debugger found at the below link.

https://jwt.io/

https://jwt.io/
I Create a new global variable named JWT, paste the generated JWT token from the POST/token request into the Initial Value field.
J For each request in Postman, besides the POST/token request, change the call's Authorization from Inherit auth from parent…

For each request in Postman, besides the POST/token request, change the call's Authorization from Inherit auth from parent to Bearer Token. Set the JWT variable as the token value, as shown in the below image.

Request Authorization for 12.5 Lab - Securing API With JWT. Use the adjacent task instructions to identify the required controls, output, or debugger values.
Request Authorization
K Send the GET/titles request, you should receive a 200 OK request with the payload of titles.
Deploy and document the secured service
Acceptance Criteria
Acceptance criteria
  • The deployed service supports the new DELETE and token operations.
  • The endpoint document describes the new calls and course test credentials required for review.
  • The required ZIP contains the completed project copy and Word document, with node_modules removed.
Instructions
7.Deploy your updated application.
A Run your previously created build script.
B Deploy your updated application to the host created in 12.4 lab using FTP.
C Check your work.
1 In Postman, change your environment to your Production environment, ensure all calls still function as they should.
8.Add the newly created calls to the document of API calls created in the 12.4 lab.
A Add the newly created calls to the document of API calls created in the 12.4 lab.
9.Submit a zipped folder of a copy of your application containing your completed application and the Word document, and with…

Submit a zipped folder of a copy of your application containing your completed application and the Word document, and with the node_modules folder removed.

A Submit a zipped folder of a copy of your application containing your completed application and the Word document, and with…

Submit a zipped folder of a copy of your application containing your completed application and the Word document, and with the node_modules folder removed.

Completion review
10.Review the feature acceptance criteria
A Confirm the required results

Return to each story and verify its acceptance criteria. Preserve the submission format and destination stated in the activity; a reading, setup guide, lab, video demonstration, and oral final may require different evidence.

Delete a record Issue and validate JSON Web Tokens Protect and verify the API endpoints Deploy and document the secured service