This repository contains two subprojects, one is the backend of the app which is a Java-SpringBoot-PostgreSQL-Gradle application, while the other is the frontend of the app which is a TypeScript-Angular-NPM app.
To run the project you will first need to setup the env file with the database
configuration. Then you can run it with
git clone https://github.com/bytestrick/tabula.git
cd tabula
./run.shWe use Spring Security and JSON Web Tokens to authenticate users. Here is the authentication flow
- Initially a client sends a
POSTrequest to the/api/v1/auth/sign-inendpoint with email and password in clear text(we assume the use of TLS). - It is the responsibility of the
AuthenticationManagerto authenticate the user credentials in the form of aUsernamePasswordAuthenticationToken. To do this it uses aDaoAuthenticationProviderwhich in turn obtains the stored user data from aDaoUserDetailsService. TheDaoUserDetailsServiceretrieves the user data from the underlying database through theUserDao. If theAuthenticationManagercan retrieve theUserDetailsthen it will compare them with the ones from the sign in request with aBCryptPasswordEncoder. - If the authentication is successful we store the
UserDetailsin theSecurityContext, this will be thePrincipal, then we use theJwtProviderto generate a new token which is sent back as the response to the sign-in request. The client will store the token in thelocalStorage.
flowchart LR
1([API client]) -- "POST /auth/sign-in with email and password" --> 2([API server])
2 <-- Compare credentials against database record --> 3[(Database)]
2 -- 200 with the newly issued JWT --> 1
All subsequent requests to the server will have the Authorization header set to Bearer <token>, this is done by the
authInterceptor in the frontend. On the server side the JwtAuthenticationFilter will intercept all requests that
need authorization, extract the JWT from them and verify it with the JwtProvider. If the verification is successful
the request is accepted otherwise the server responds with HTTP 401.
flowchart LR
1([API client]) -- "GET /resource
Authorization: Bearer {JWT}" --> 2([API server])
2 -- Verifies token in request header --> 3[JwtProvider]
2 -- OK if JWT is valid,
else 401 --> 1
JwtProvider uses a Message Authentication (MAC) signer to sign the JWT and MAC verifier to verify it. These are
created using a high entropy secret that is provided throuh an environment variable and is not to be exposed.
Before running the backend you must provide it some secrets. It requires a high entropy secret of at least 256 bits to
generate the keys to sign and verify JWTs (see JwtProvider), you can generate this with
openssl rand --hex 32Moreover, it requires datasource credentials and email credentials to send emails to verify the user emails at sign-up and such. If you decide to provide a Gmail account you can create an app password and provide it directly in double quotes.
The .env file is placed in backend/src/main/resources/ next to applications.properties. An example file:
JWT_SECRET=312a8d917893986c44f7f9dea03f0b045a40281f2cb1762082f50348d4e30b50
EMAIL_ADDRESS=tabula.noreply@example.com
EMAIL_PASSWORD="vejr zffv tcda mrqw"
DATASOURCE_URL=jdbc:postgresql://localhost:5432/tabula
DATASOURCE_USERNAME=postgres
DATASOURCE_PASSWORD=postgres