-
Notifications
You must be signed in to change notification settings - Fork 21
Authentication Flow
We are using Google OAuth to easily authenticate users into our application. Listed below are the steps of the authenticating process.
- When the user clicks on the 'Sign in with Google' button on the main screen of our React application (
/login
), they are redirected to a page where they can enter their Google credentials. - If the credentials are successful, Google provides the application with an
id_token
which is a JWT token that identifies our user. - Thereafter, this token is sent to the backend server, where we verify the authenticity of the JWT using the Google library.
- If the token is invalid, it is discarded and an HTTP status code of 401 is returned.
- If the token is valid, further checks are done using the user's email address to check whether the user has logged in previously and is registered in the database.
- If the user has not been registered before in our database, then new resources are created for the user such as a new
User
entity instance. - Thereafter, a custom JWT is created by our system and returned back to the client, so it can be attached with the subsequent requests. An HTTP status code of 200 is returned.
- If the user has not been registered before in our database, then new resources are created for the user such as a new
A subset of this process is illustrated using the diagram below.
To validate Google ID tokens you will need to use their API. To set up an account for this, see here
The responsibility of AuthContextProvider
is to ensure that only the authenticated users can get access to the restricted parts of the website.
When a user logs into the application from the Login
page component, we invoke the setTokenId
method in the onSuccess
callback which takes the JWT given to us by Google. When the token ID is updated in AuthContextProvider
using setTokenId
, there will be a backend call to /api/users/login
. In this, the backend would generate an internal custom JWT and return it to the client application. This JWT is then persisted from AuthContextProvider
using the local storage of the browser, so it can be transferred in the subsequent requests.
When the backend validates a JWT, the frontend application changes the global axios
object so that all outgoing requests are attached with an authorization
header.
We are using the AuthRoute
component (along with <Outlet/>
) to display routes that require user authentication. This component uses the context mentioned above to check whether the user has been authenticated.
All requests made to the API are pre-handled in order to validate the JWT used to authenticate the request (except for POST: api/users/login, since users do not have a JWT when hitting that endpoint; it is the one that generates and provides the JWTs). This is done in the interceptor.UserInterceptor
class, which extends Spring's HandlerInterceptor. The token is taken from the request and validated in the JwtTokenUtil
class, which parses the claims using a secret. If the token is rendered valid, then the client User ID is stored in the HTTP session and the request is passed on to the appropriate controller.
-
GET:/api/validate
- Authorization Header: token
- Response
- 201 No Content: when the token is valid
- 401 Unauthorized: when the token is invalid
-
POST:/api/users/login
- Authorization header: Google ID token
- Response:
- 200 OK: ID token validated, login success
- 401 Unauthorized: invalid ID token
- Response payload:
- custom JWT if success, to be used for future requests.
General Information
Project Details
Implementation Details