Awesome Open Source
Awesome Open Source

Jwt-Spring-Security-JPA

Travis (.org) GitHub

A demo project explaining the backend authentication using JWT (Json Web Token) authentication using Spring Security & MySQL JPA.

There's support for the following features:

  • Conventional email/username based registration with admin support.
  • Conventional Login using Spring Security and generation of JWT token.
  • Multiple device login and logout support.
  • In memory store for blacklisting JWT tokens upon user logout.
  • Expiration bases email verification. Mail is sent upon registration.
  • Resend the email confirmation email if old one expires.
  • Forgot-password functionality with password reset token validations.
  • Admin protected urls leveraging Spring security.
  • Refresh JWT tokens once the temporary JWT expires.
  • Check availability of username/email during registration.


JWT

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.


Swagger Docs

The project has been configured with a Swagger docket that exposes the APIs with the schema

Accessible at http://localhost:9004/swagger-ui.html once the app is running.

image


Exception Handling

  • The app throws custom exceptions wherever necessary which are captured through a controller advice. It then returns the appropriate error response to the caller
  • Moreover, entities are validated using JSR-303 Validation constraints.

Getting Started

Clone the application

$ git clone https://github.com/isopropylcyanide/Jwt-Spring-Security-JPA.git
$ cd Jwt-Spring-Security-JPA

Create a MySQL database

$ create database login_db

Change MySQL username and password as per your MySQL installation

  • Edit spring.datasource.username and spring.datasource.password properties as per your mysql installation in src/main/resources/application.properties
  • Edit spring.mail.username and spring.mail.password properties as per your mail server src/main/resources/mail.properties

Run the app

./mvnw spring-boot:run   # For UNIX/Linux based operating systems
mvnw.cmd spring-boot:run # For Windows based operating systems
  • The server will start on server.port:9004 and will create the tables for you.
  • Every run of the app will reset your state. To not do that, modify spring.jpa.hibernate.ddl-auto: update

API

Registering a User
curl --location --request POST 'localhost:9004/api/auth/register' \
--header 'Content-Type: application/json' \
--data-raw '{
    "email": "[email protected]",
    "password": "amangarg",
    "registerAsAdmin": true
}'

image

⚠️ If you re-register an email twice, you'll get the "email in use" error


Logging in an unverified user
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"
    }
}'

image


Confirming the user email verification token
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"
    }
}'

image

⚠️ 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


Logging in the user with valid credentials
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"
    }
}'

image

⚠️ 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.


Using the JWT token to access a user resource
curl --location --request GET 'localhost:9004/api/user/me' \
--header 'Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNjM1NjE0NTY4LCJleHAiOjE2MzU2MTU0Njh9.d8CJYduoC44njutphODoezheSt_so3Doc9g1RSiMaDU_qJwY0_3Ym4092hFkHsh-jbyB_9i66LbwSEE-szAgEw'

image

⚠️ 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.

image


Using the JWT token to access an admin resource
curl --location --request GET 'localhost:9004/api/user/admins' \
--header 'Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNjM1NjE0NTY4LCJleHAiOjE2MzU2MTU0Njh9.d8CJYduoC44njutphODoezheSt_so3Doc9g1RSiMaDU_qJwY0_3Ym4092hFkHsh-jbyB_9i66LbwSEE-szAgEw'

image

⚠️ If you registered a user with registerAsAdmin: false, then you will get a "Forbidden" error. image

⚠️ JWT has to be valid (same constraints as the above user resource API)


Logout user
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"
    }
}'

image

❔ 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.

image

⚠️ 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

image

⚠️ If you try to log out a logged-in user against an invalid device (say D2), you will get an "Invalid Device" error.

image


Request a reset password link for a registered user
curl --location --request POST 'localhost:9004/api/auth/password/resetlink' \
--header 'Content-Type: application/json' \
--data-raw '{
    "email": "[email protected]"
}'

image

❔ 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


Reset password for a registered user
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"
}'

image

⚠️ 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.

image

⚠️ If you try to use a password reset token twice, you will get a "Token Inactive" error image


Refreshing the JWT token for longer login sessions
curl --location --request POST 'localhost:9004/api/auth/refresh' \
--header 'Content-Type: application/json' \
--data-raw '{
    "refreshToken": "d029e0fa-80f5-4768-837c-7e85a0f94960"
}'

image

❔ 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

image


Check email in use
curl --location --request GET 'localhost:9004/api/auth/[email protected]'

image

❔ 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


Roles

  • The spring boot app uses role based authorization powered by spring security
  • Tables and role data should have been created by default upon the first startup.
  • Any new user who signs up to the app is assigned the ROLE_USER by default.
  • In case the role entries aren't created, please execute the following sql queries in the database to insert the USER and ADMIN roles.
INSERT INTO `login_db.role` (ROLE_NAME)
VALUES ('ROLE_USER');
INSERT INTO `login_db.role` (ROLE_NAME)
VALUES ('ROLE_ADMIN');

Contribution

  • Remember, the project is a demo and should not be used into production directly.
  • Please fork the project and adapt it to your use case.
  • Submit a pull request with proper motivation and test plan.
  • Postman collection dump available here)
  • Not everything is in scope for this demo project. Feel free to fork the project and extend the functionality.
  • Project is equipped with a JUnit but lacks tests in most places. Would really appreciate your contributions here.



Alternative Project Comparisons
Related Awesome Lists
Top Programming Languages

Get A Weekly Email With Trending Projects For These Topics
No Spam. Unsubscribe easily at any time.
Java (404,180
Security (32,120
Mysql (32,036
Token (29,461
Spring (28,716
Email (21,270
Jwt (10,163
Jpa (3,523
Jwt Authentication (2,212
Spring Security (1,876
Jwt Token (903
Refresh Token (97
Spring Security Jwt (62
Spring Security Web (10
Spring Boot Jpa (6
Spring Events (5