Backend MVP showcasing JWT (Json Web Token) authentication with multiple login, timeout / refresh / logout (with in memory invalidation) using Spring Security & MySQL JPA.
There's support for the following features:
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
The project has been configured with a Swagger docket that exposes the APIs with the schema
Accessible at http://localhost:9004/swagger-ui
once the app is running.
$ git clone https://github.com/isopropylcyanide/Jwt-Spring-Security-JPA.git
$ cd Jwt-Spring-Security-JPA
$ create database login_db
spring.datasource.username
and spring.datasource.password
properties as per your mysql installation
in src/main/resources/application.properties
spring.mail.username
and spring.mail.password
properties as per your mail
server src/main/resources/mail.properties
./mvnw spring-boot:run # For UNIX/Linux based operating systems
mvnw.cmd spring-boot:run # For Windows based operating systems
server.port:9004
and will create the tables for you.spring.jpa.hibernate.ddl-auto: update
curl --location --request POST 'localhost:9004/api/auth/register' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "[email protected]",
"password": "amangarg",
"registerAsAdmin": true
}'
⚠️ If you re-register an email twice, you'll get the "email in use" error
curl --location --request POST 'localhost:9004/api/auth/login' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "[email protected]",
"password": "amangarg",
"deviceInfo": {
"deviceId": "D1",
"deviceType": "DEVICE_TYPE_ANDROID",
"notificationToken": "N1"
}
}'
curl --location --request GET 'localhost:9004/api/auth/registrationConfirmation?token=bcbf8764-dbf2-4676-9ebd-2c74436293b9' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "[email protected]",
"password": "HI12",
"deviceInfo": {
"deviceId": "D1",
"deviceType": "DEVICE_TYPE_ANDROID",
"notificationToken": "N1"
}
}'
⚠️ If you pass the incorrect token you will get a "Token Mismatch error"
❔ Don't know the token?: Check your email in
mail.properties
❔ Still didn't get it?: Look inside the database
email_verification_token#token
curl --location --request POST 'localhost:9004/api/auth/login' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "[email protected]",
"password": "amangarg",
"deviceInfo": {
"deviceId": "D1",
"deviceType": "DEVICE_TYPE_ANDROID",
"notificationToken": "N1"
}
}'
⚠️ If you do not enter correct credentials you will get a "Bad credentials error"
⚠️ If your email is not verified (refer the above API) you will get an "Unauthorized" error
❔ Device information is required to enable a multi device login and logout functionality.
curl --location --request GET 'localhost:9004/api/user/me' \
--header 'Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNjM1NjE0NTY4LCJleHAiOjE2MzU2MTU0Njh9.d8CJYduoC44njutphODoezheSt_so3Doc9g1RSiMaDU_qJwY0_3Ym4092hFkHsh-jbyB_9i66LbwSEE-szAgEw'
⚠️ If you enter an invalid token (obtained post login), you will get an "Incorrect JWT Signature" error.
⚠️ If you enter a malformed JWT token, you will get a "Malformed JWT Signature" error.
⚠️ If you enter an expired JWT token (default:
app.jwt.expiration
, you will get an "Expired JWT Signature" error and clients should refresh the JWT token.
curl --location --request GET 'localhost:9004/api/user/admins' \
--header 'Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNjM1NjE0NTY4LCJleHAiOjE2MzU2MTU0Njh9.d8CJYduoC44njutphODoezheSt_so3Doc9g1RSiMaDU_qJwY0_3Ym4092hFkHsh-jbyB_9i66LbwSEE-szAgEw'
⚠️ If you registered a user with
registerAsAdmin: false
, then you will get a "Forbidden" error.
⚠️ JWT has to be valid (same constraints as the above user resource API)
curl --location --request POST 'localhost:9004/api/user/logout' \
--header 'Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNjM1NjE0NTY4LCJleHAiOjE2MzU2MTU0Njh9.d8CJYduoC44njutphODoezheSt_so3Doc9g1RSiMaDU_qJwY0_3Ym4092hFkHsh-jbyB_9i66LbwSEE-szAgEw' \
--header 'Content-Type: application/json' \
--data-raw '{
"deviceInfo": {
"deviceId": "D1",
"deviceType": "DEVICE_TYPE_ANDROID",
"notificationToken": "N1"
}
}'
❔ Logging out also deletes the refresh token associated with the device. In real production, this token should be specifically invalidated.
⚠️ If the JWT isn't passed then you will get an "Unauthorized" error.
⚠️ If you try to log out same user twice (without an app restart), you will get a "Token Expired" error. This works because on logout we invalidate the JWT
⚠️ If you try to log out a logged-in user against an invalid device (say D2), you will get an "Invalid Device" error.
curl --location --request POST 'localhost:9004/api/auth/password/resetlink' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "[email protected]"
}'
❔ You can request a password reset multiple times. The reset token would be generated multiple times with an
app.token.password.reset.duration
❔ You can request a password reset for a user even when they have not verified their email once. This is okay for our demo case.
⚠️ If you try to request a password reset for an unregistered user, you will get a "No matching user" error
curl --location --request POST 'localhost:9004/api/auth/password/reset' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "[email protected]",
"password": "P1",
"confirmPassword": "P1",
"token": "880ab6f1-4b4b-4d04-92bd-8995b4063205"
}'
⚠️ If your new passwords do not match, there will be an error
⚠️ If your password reset token is not valid or is for some other user, you'll get a "Password Reset Token Not Found" error.
⚠️ If you try to use a password reset token twice, you will get a "Token Inactive" error
curl --location --request POST 'localhost:9004/api/auth/refresh' \
--header 'Content-Type: application/json' \
--data-raw '{
"refreshToken": "d029e0fa-80f5-4768-837c-7e85a0f94960"
}'
❔ You can refresh a JWT multiple times against the refresh token. That is the purpose of refresh. Refresh token expiry can be controlled with
app.token.refresh.duration
⚠️ If you pass an invalid refresh token (obtained through login), you will get a "No token found" error
curl --location --request GET 'localhost:9004/api/auth/[email protected]'
❔ The API can be accessed insecurely and hence should be rate limited in production to prevent a DDOS attack.
❔ You can request a password reset for a user even when they have not verified their email once. This is okay for our demo case.
⚠️ If you try to request a password reset for an unregistered user, you will get a "No matching user" error
ROLE_USER
by default.USER
and ADMIN
roles.INSERT INTO `login_db.role` (ROLE_NAME)
VALUES ('ROLE_USER');
INSERT INTO `login_db.role` (ROLE_NAME)
VALUES ('ROLE_ADMIN');