I recently had to convert an application from using session-based authentication, managed by passport and Sails.js on the server, to JWT authentication. This is a quick breakdown of the steps used to accomplish this.
Let's start on the Angular side of the application. So for authentication, we need our Angular application to get a token from the authentication server, store it, and attach it to each outgoing HTTP call. In this case, we were using an Open ID Connect authentication server, but the same principles would apply to other authentication servers and protocols. We also want to use guards to protect routes that the user should be authenticated to use.
Authenticating with the Auth Server
To authenticate the Angular application, we used the
angular-auth-oidc-client package. The installation instructions are really clear, and by following them you should be able to get the authentication going. For brevity, I'm not going to repeat those instructions here. I will note one thing, however, and that is that the configuration for the package should be done in the root
AppModule of your Angular app. I tried to do it in a separate module, even one that was imported to the root module, and couldn't get it working. So I'd just stick with doing it in the root module like the configuration shows.
Angular HTTP Interceptors
After authenticating and getting a token back, the next step is to attach that token to every outgoing HTTP call. Again, this has been covered in many, many blog posts. I'm going to refer to this one by Paul Halliday. I won't go over every single thing to accomplish this, but basically you should make all HTTP calls using the new
@angular/common/http. You can then
intercept each call and in the process attach the token as an authorization header. Write the code once, apply it to all outgoing calls. Nice and simple.
Angular Route Guards
Lastly, on the front end, make sure to add route guards to all routes that require authentication. In the guard, you can use the security service that you've decided to use to check if the user is authenticated. If they are, allow them to continue. If not, stop the routing and send them to a login page. Check out this article on route guards if you need some help writing your own.
This information is talking specifically about how to authenticate using the Sails.js framework, but Sails is written on top of Express, so the same steps can be used in an Express app, and likely can be used in any number of applications. When a call comes in from the server, we want to make sure that the call has a valid authorization header and token. We need to verify and decode that token, and then move forward. If they are not authenticated, return a forbidden status code; if they are authenticated, continue on in the workflow.
Sails.js has a nice feature called policies. Basically they allow you to run code prior to the controller function for a route. This makes it easy for us to make sure each call is authenticated. In the
config/policies.js file, add the following:
'*': 'yourPolicyName'. In this case, the policy should be named something that signifies authentication. Now every call will go through this policy and the code will run. You can be more specific, or exclude routes from going through this policy if needed, based on use case.
Inside the policy, we should check the
request for an authorization header and the token. Return a forbidden status code if it's not found. Next, use a package to verify the token. In our case, we used the
jsonwebtoken package from Auth0. There is a
verify function that takes the token from the front end as the first parameter, and a secret or public key from the auth server as the second parameter. The third parameter is a callback function with two parameters. The first is an error (null if everything was okay), and the second is the decoded user (if there wasn't an error). The only hold up we had here was getting the public key in the correct format to make sure that we could verify the token.
To overcome this, we installed one more package,
openssl-cert-tools. It allows you to put in a host name and port for the auth server. Then, with the callback, it returns the PEM encoded certificate. That certificate can be used in the
verify function. After calling
verify(token, certificate) with the callback, you should have the decoded user. If you don't, again return a forbidden status code. If everything is good, continue on. You can save the decoded user for later use if needed as well.
It took a little bit of work to get the authentication working, but overall it was quite simple. The authentication token comes back from the auth server, is passed to your application server from the Angular app, the application server verifies it, and then everything continues on. Hopefully this has been helpful. Reach out (using the links below) if you have any questions!