0
votes

I have a frontend application (Vue.js) secured with @azure/msal-browser. I have an "app registration" in Azure validating if a user is part of our AD and if he/she is allowed to acces the webpage. This works fine. A user needs to authenticate first before he/she can access the webpages.

The webpage itself executes several AJAX calls towards a REST API, which is nothing more than a Spring Boot application with Spring Web enabled. This backend is not secured ... yet (!).

Goal:

I want to secure the backend so that you can only query the REST API if you have a valid access token. The frontend will add the Authentication: Bearer XXX token when performing calls towards the backend.

I had a look at the examples over here: https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/spring/azure-spring-boot-samples and I see 4 options:

  • azure-spring-boot-sample-active-directory-resource-server-by-filter-stateless
  • azure-spring-boot-sample-active-directory-resource-server-by-filter
  • azure-spring-boot-sample-active-directory-resource-server-obo
  • azure-spring-boot-sample-active-directory-resource-server

The first 2 seem a bit outdated, because if I run them locally, I get all deprecated warnings. But which of these examples would cover my usecase?

Summarized:

I want to ...

  • ... authorize the user in the frontend using msal-browser
  • ... perform requests from the frontend (well, the client browser executing AJAX calls) towards the backend by just adding the access token as header
  • ... the backend automatically validates the token and if valid, the backend returns the data

Thanks for any feedback!

1

1 Answers

0
votes

I had the same idea before, and here's my solution.

In the front end, using msal.js provided by microsoft to achieve the login module and generate access token via it. Then sending ajax request with Bearer token in the request header. While in my springboot backend application, refer to this sample to add filter to validate if the incoming request has a effective access token in it. The filter need to check if there's a token in the request header then decode it and check if the token is expired, whether has correct scope if needed.

Here comes with another task that we need to set scope for the backend api. This needs to expose api in azure ad(creating azure ad app and go to expose api blade to do the next actions). You can refer to this for details. Pls note here, the api exposed here is used to be a input parameter when generating access token(we need to set scope parameter, and the exposed api is the scope after adding this api permission to the azure ad app used to generate access token).

After these actions, the filter can check if the token contains the scope you customed before. I think this is the most important to validate the token.

Filter code: https://stackoverflow.com/a/66097364/14574199

And because of the frontend-backend model, you may need a CORS filter for your springboot application like this:

package com.example.demo;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Order(Integer.MIN_VALUE)
@Component
public class CorsFilter implements Filter {
    @Override
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
          throws IOException, ServletException {
        HttpServletResponse res = (HttpServletResponse) response;
        res.addHeader("Access-Control-Allow-Credentials", "true");
        res.addHeader("Access-Control-Allow-Origin", "*");
        res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
        res.addHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN,Authorization");
        if (((HttpServletRequest) request).getMethod().equals("OPTIONS")) {
          response.getWriter().println("ok");
          return;
        }
        chain.doFilter(request, response);
      }
      @Override
      public void destroy() {
      }
      @Override
      public void init(FilterConfig filterConfig) throws ServletException {
      }
}