Secure Authentication for Web Apps & APIs Using JWTs
Table of Contents
JSON Web Tokens
Getting StartedRyan Chenkie opens the course by sharing the agenda and discussing the some of the topics he’ll be covering. While Ryan will be using an Angular 2 application throughout the course, he provides links to Angular 1 and React versions as well. The API for is hosted on now.sh, but if participants want a local copy, Ryan provides a GIthub repository which can be downloaded.
Application & API DemonstrationRyan spends a few minutes demonstrating the application used throughout the course. The application displays a list of instructors. Users must be authenticated to view this list. Authenticated users with admin privileges will be able to add instructors. He also takes a look at the API which provides the application with data.
Challenge: Run the Finished ApplicationIn this challenge, you will run the finished application in the framework of your choosing. You will also access the API and view it’s documentation. Optionally, you can create a user for yourself by using a utility like Postman.
JSON Web Token BasicsA JSON Web Token, or JWT, is an open standard for transferring data securely between two parties in a JSON payload. JWTs are digitally signed and a great mechanism for implementing stateless authentication.
Challenge: Inspecting a JWTIn this challenge, you will spend a few minutes inspecting the JSON web token you were given during the account creation process in the application. The site jwt.io provides a debugger which can decode a JWT.
JSON Web Tokens & Single Page Applications
Traditional AuthenticationBefore diving deeper into how JSON Web Tokens interact with single page applications, Ryan reviews how authentication works in a traditional client-server application. He explains how servers manage sessions and cookies. He also demonstrates the session management on FrontendMasters.com and talks through the drawbacks of cookie/session authentication.
JWT AuthenticationRyan now explains how JWTs are used to authenticate single page applications. When credentials are submitted the server signs and returns a token which is saved on the client. Every HTTP request moving forward will send the token as an Authorization header. This eliminates the need for the server to perform costly database lookups for verifying user access.
Securing the PayloadWhile answering a couple audience questions, Ryan takes some time to walk through how the server signs and secures the payload. He also talks about why sensitive data should not be stored in the payload and some strategies for storing the token secret in an environment variable.
Implementing Authentication with JWTAs Ryan described earlier, when a single page application submits credentials to the server, a JWT will be signed and returned in the response. Ryan walks through some application code which demonstrates how a user submits credentials, is authenticated, and how the subsequent JWT is stored on the client.
Challenge: Creating the Login & SignupIn this challenge, you will create the login and signup screens which will make POST requests with the user’s credentials. You will store the returned JWT in local storage and create a logout button which will remove the saved token from storage. The starting files for this challenge are on the 01-implementing-auth-starter branch.
Creating the Login & Signup SolutionRyan walks through the solution to the challenge which creates the login and signup screens. The code for the solution is on the 01-implementing-auth-solution branch.
Client Side Sessions
Server-Side vs. Client-Side SessionsLukas Ruebbelke takes over to talk about client-side sessions. In general, sessions are a way to persevere a desired state. Whether managed by the server or by a JWT, a session can confirm a user’s authentication status. Lukas spends a few minutes comparing server-side and client-side sessions. Afterward, he talks through an example of implementing a client-side session with JWT’s in a single page application.
Q&A: JWT’s and SecurityBefore moving into the next challenge, Ryan and Lukas spend a few minutes answering audience questions about security best-practices with JWT’s. These questions include short vs. long expiring tokens, securely storing tokens on the client, payload security, and avoiding cross-site scripting.
Challenge: Expire TimeIn this challenge, you will use the JWT’s expire time to check whether the user is authenticated. You will also conditionally hide/show elements based on the authentication state. The starting code for this challenge is on the 02-client-sessions-starter branch.
Expire Time SolutionRyan walks through the solution to the JWT Expire Time challenge. The solution code for this challenge is on the 02-client-sessions-solution branch.
User Information in the Payload
Decoding a JWTRyan briefly reviews what is contained in a JWT’s playload. The payload contains “claims” which are assertions about a subject. For example, in the case of a user, the claims might include name, email, and profile image. To access the payload of a JWT, developers typically use a helper function which decodes the JWT and provides an API for accessing the payload.
Payload Best PracticesIt’s a best practice to keep the payload small since it is sent to the server with each request. The payload should also be free of sensitive information since it can be easily decoded. In the case where a large amount of user data is needed, it’s best to create a separate endpoint.
Challenge: Reading the PayloadIn this challenge, you will read the user’s profile out of the JWT payload. Once the profile data is read, you will display the user’s details in a Profile view. The starting file for this challenge are on the 03-user-profile-starter branch.
Reading the Payload SolutionRyan walks through the solution to the Reading the Payload challenge. The code for the solution is on the 03-user-profile-solution branch.
How JWT’s Protect ResourcesA user’s level of authentication in an application will determine which resources they can access. To protect resources from non-authenticated users, and endpoint can be created which requires the presence of a JWT. Any resources requested will have its header inspected by the API to validate the current user.
Making Authenticated RequestsRyan looks at some sample code for making authenticated requests from a client application. Authenticated requests will retrieve the JWT from storage and attach it as an Authentication header. This can occur on every request, only certain types of requests, or on a case-by-case basis.
Challenge: Protecting ResourcesIn this challenge, you will set up the application to send the JWT in an Authorization header when necessary. You’ll also make a GET request to the API for retrieving a list of instructors and a POST request to the same API to create a new instructor. The starting code for this challenge is on the 04-protecting-resources-starter branch.
Protecting Resources Challenge SolutionRyan walks through the solution to the Protecting Resources challenge. The code for the solution is on the 04-protecting-resources-solution branch.
Client-Side ConsiderationsProtecting resources and routes on the client-side can sometimes be difficult because savvy users could potentially modify a JWT. Since the server is not present to verify the signature, the client application might assume the user is authenticated or has the required scope. Lukas talks through some of these client-side considerations and shares some strategies to prevent unauthenticated access.
How Client-Side Routes are ProtectedLukas walks through a code example which highlights one way client-side routes can be protected from unauthenticated access. In the example, parameters are included in the route to specify a “canActivate” object which supplies the conditions which must be satisfied by a user in order to gain access to the route.
Challenge: Protecting RoutesIn this challenge, you will verify the user’s JWT is unexpired before the transition to the instructors route occurs. You will also verify the user has an admin scope when navigating to the instructor/new route. The code for starting this challenge is on the 05-protecting-routes branch.
Protecting Routes SolutionRyan walks through the solution to the Protecting Routes challenge. The code for the solution is on the 05-protecting-routes-solution branch.
Further Reading & Wrap-UpRyan wraps up the course by talking through some important security considerations. He also talks about how implementations of JWTs can vary when using standards like OAuth 2.0 or OIDC. Finally, Ryan shares some resources learning more about securing web applications and JWT’s.