Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot and vueJs authentication with firebase

I am trying to implement authentication spring boot in backend and vue Js in front end, the problem is that I have ly backkend connected readonly to a database, so used authentication using vue js and firebase authentication feature.

The problem is that my endpoints still accessible and anyone can send requests and fetch data using postman for example !

If anyone has an idea how to resolve that please go ahead, thanks !

PS: i don't think that i may help, but here is my login code anyway, @Renaud Tarnec

import firebase from 'firebase'

export default {
  name: 'login',
  data: function() {
    return {
      email: '',
      password: ''
    }
  },
  methods: {
    signIn: function() {
      firebase.auth().signInWithEmailAndPassword(this.email, this.password).then(
        function(user) {
          alert('You are connected')
        },
        function(err) {
          aler('Ooops,' + err.message)
        }
      );
    }
  }
}
</script>

and here is for example a part of my repo and there is list of events :

@RequestMapping("api/events")
public class EventController {
    @Autowired
    private EventRepository eventrepository;

    @GetMapping
    public ArrayList<Event> find() {
        ArrayList<Event> events = new ArrayList<Event>();
        for (Event e : eventrepository.findAll()) {
            System.out.println(e);
            events.add(e);
        }
        return events;
    }
like image 728
Aleb Avatar asked Jan 27 '23 15:01

Aleb


1 Answers

This is normal behaviour, because you send your request against firestore with the admin sdk credentials.

You need to put some authentication into your spring boot application.

I put some code together that put all your requests behind firebase authentication.

FirebaseConfig.java

@Configuration
@EnableConfigurationProperties
@ConfigurationProperties(prefix="firebase")
public class FirebaseConfig {

    private static final Logger logger = LoggerFactory.getLogger(FirebaseConfig.class);

    private String databaseURL;
    private String serviceAccount;

    @Bean
    public DatabaseReference firebaseDatabse() {
        DatabaseReference firebase = FirebaseDatabase.getInstance().getReference();
        return firebase;
    }

    @PostConstruct
    public void init() {

        try {
            FirebaseApp.getInstance();
        } catch (IllegalStateException e) {
            try {
                InputStream inputStream = FirebaseConfig.class.getClassLoader().getResourceAsStream(serviceAccount);

                try {
                    FirebaseOptions options = new FirebaseOptions.Builder().setCredentials(GoogleCredentials.fromStream(inputStream))
                            .setDatabaseUrl(databaseURL).build();

                    FirebaseApp.initializeApp(options);
                } catch (IOException ioE) {
                    ioE.printStackTrace();
                }
            } catch (NullPointerException nullE) {
                nullE.printStackTrace();
            }
        }

    }

    public String getDatabaseURL() {
        return databaseURL;
    }

    public void setDatabaseURL(String databaseURL) {
        this.databaseURL = databaseURL;
    }

    public String getServiceAccount() {
        return serviceAccount;
    }

    public void setServiceAccount(String serviceAccount) {
        this.serviceAccount = serviceAccount;
    }
}

Then you need to enable web security:

WebSecurityConfig.java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private static final Logger logger = LoggerFactory.getLogger(WebSecurityConfiguration.class);

    /**
     * Use to create instance of {@link FirebaseAuthenticationTokenFilter}.
     *
     * @return instance of {@link FirebaseAuthenticationTokenFilter}
     */
    public FirebaseAuthenticationTokenFilter firebaseAuthenticationFilterBean() throws Exception {
        logger.debug(
                "firebaseAuthenticationFilterBean():: creating instance of FirebaseAuthenticationFilter.");

        FirebaseAuthenticationTokenFilter authenticationTokenFilter = new FirebaseAuthenticationTokenFilter();

        return authenticationTokenFilter;
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {

        httpSecurity
                .cors()
                .and()
                .csrf()
                .disable()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // Custom security filter
        httpSecurity.addFilterBefore(firebaseAuthenticationFilterBean(),
                UsernamePasswordAuthenticationFilter.class);
    }

}

Lastly you add a request filter that validates the access token every time you make a request against the api.

FirebaseAuthenticationTokenFilter.java

@Component
public class FirebaseAuthenticationTokenFilter extends OncePerRequestFilter {

    private static final Logger logger = LoggerFactory.getLogger(FirebaseAuthenticationTokenFilter.class);
    private final static String TOKEN_HEADER = "Authorization";

    /**
     *
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        logger.debug("doFilter:: authenticating...");

        HttpServletRequest httpRequest = request;
        String authToken = httpRequest.getHeader(TOKEN_HEADER);

        if (Strings.isNullOrEmpty(authToken)) {
            filterChain.doFilter(request, response);
            return;
        }

        try {
            Authentication authentication = getAndValidateAuthentication(authToken);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            logger.debug("doFilter():: successfully authenticated.");
        } catch (Exception ex) {
            HttpServletResponse httpResponse = response;
            httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            logger.debug("Fail to authenticate.", ex);
        }

        filterChain.doFilter(request, response);
    }

    /**
     *
     * @param authToken Firebase access token string
     * @return the computed result
     * @throws Exception
     */
    private Authentication getAndValidateAuthentication(String authToken) throws Exception {
        Authentication authentication;

        FirebaseToken firebaseToken = authenticateFirebaseToken(authToken);
        authentication = new UsernamePasswordAuthenticationToken(firebaseToken, authToken, new ArrayList<>());

        return authentication;
    }

    /**
     * @param authToken Firebase access token string
     * @return the computed result
     * @throws Exception
     */
    private FirebaseToken authenticateFirebaseToken(String authToken) throws Exception {
        ApiFuture<FirebaseToken> app = FirebaseAuth.getInstance().verifyIdTokenAsync(authToken);

        return app.get();
    }

    @Override
    public void destroy() {
        logger.debug("destroy():: invoke");
    }

}

Now your API endpoints are save against unauthorized requests.

In you web application you handle the authorization as normally with firebase. On every request to the spring-boot application you pass the access token as Authorization header.

Keep in mind that this is not really save, because the spring boot API acts as an admin against the firebase SDK.

like image 179
Kersten Avatar answered Jan 31 '23 09:01

Kersten