Giter Site home page Giter Site logo

ivangfr / springboot-react-keycloak Goto Github PK

View Code? Open in Web Editor NEW
400.0 15.0 157.0 50.97 MB

The goal of this project is to secure movies-app using Keycloak (with PKCE). movies-app consists of two applications: one is a Spring Boot Rest API called movies-api and another is a React application called movies-ui.

Java 32.76% HTML 2.30% JavaScript 50.31% CSS 0.57% Shell 14.06%
spring-boot keycloak semantic-ui-react pkce dicebear java mongodb docker omdb-api springdoc-openapi

springboot-react-keycloak's Introduction

springboot-react-keycloak

The goal of this project is to secure movies-app using Keycloak(with PKCE). movies-app consists of two applications: one is a Spring Boot Rest API called movies-api and another is a React application called movies-ui.

Proof-of-Concepts & Articles

On ivangfr.github.io, I have compiled my Proof-of-Concepts (PoCs) and articles. You can easily search for the technology you are interested in by using the filter. Who knows, perhaps I have already implemented a PoC or written an article about what you are looking for.

Additional Readings

Project diagram

project-diagram

Applications

  • movies-api

    Spring Boot Web Java backend application that exposes a REST API to manage movies. Its secured endpoints can just be accessed if an access token (JWT) issued by Keycloak is provided.

    movies-api stores its data in a Mongo database.

    movie-api has the following endpoints:

    Endpoint Secured Roles
    GET /api/userextras/me Yes MOVIES_ADMIN and MOVIES_USER
    POST /api/userextras/me -d {avatar} Yes MOVIES_ADMIN and MOVIES_USER
    GET /api/movies No
    GET /api/movies/{imdbId} No
    POST /api/movies -d {"imdb","title","director","year","poster"} Yes MOVIES_ADMIN
    DELETE /api/movies/{imdbId} Yes MOVIES_ADMIN
    POST /api/movies/{imdbId}/comments -d {"text"} Yes MOVIES_ADMIN and MOVIES_USER
  • movies-ui

    React frontend application where users can see and comment movies and admins can manage movies. In order to access the application, user / admin must login using his/her username and password. Those credentials are handled by Keycloak. All the requests coming from movies-ui to secured endpoints in movies-api have a access token (JWT) that is generated when user / admin logs in.

    movies-ui uses Semantic UI React as CSS-styled framework.

Prerequisites

  • Java 21+

  • npm

  • Docker

  • jq

  • OMDb API KEY

    To use the Wizard option to search and add a movie, we need to get an API KEY from OMDb API. In order to do it, access https://www.omdbapi.com/apikey.aspx and follow the steps provided by the website.

    Once we have the API KEY, create a file called .env.local in springboot-react-keycloak/movies-ui folder with the following content:

    REACT_APP_OMDB_API_KEY=<your-api-key>
    

PKCE

As Keycloak supports PKCE (Proof Key for Code Exchange) since version 7.0.0, we are using it in this project.

Start Environment

In a terminal and inside springboot-react-keycloak root folder run:

./init-environment.sh

Initialize Keycloak

In a terminal and inside springboot-react-keycloak root folder run:

./init-keycloak.sh

This script will:

  • create company-services realm;
  • disable the required action Verify Profile;
  • create movies-app client;
  • create the client role MOVIES_USER for the movies-app client;
  • create the client role MOVIES_ADMIN for the movies-app client;
  • create USERS group;
  • create ADMINS group;
  • add USERS group as realm default group;
  • assign MOVIES_USER client role to USERS group;
  • assign MOVIES_USER and MOVIES_ADMIN client roles to ADMINS group;
  • create user user;
  • assign USERS group to user;
  • create admin user;
  • assign ADMINS group to admin.

Running movies-app using Maven & Npm

  • movies-api

    • Open a terminal and navigate to springboot-react-keycloak/movies-api folder;

    • Run the following Maven command to start the application:

      ./mvnw clean spring-boot:run -Dspring-boot.run.jvmArguments="-Dserver.port=9080"
      
    • We can also configure Social Identity Providers such as, GitHub, Google, Facebook and Instagram. I've written two articles in Medium where I explain step-by-step how to integrate GitHub and Google.

  • movies-ui

    • Open another terminal and navigate to springboot-react-keycloak/movies-ui folder;

    • Run the command below if you are running the application for the first time:

      npm install
      
    • Run the npm command below to start the application:

      npm start
      

Applications URLs

Application URL Credentials
movie-api http://localhost:9080/swagger-ui.html Access Token
movie-ui http://localhost:3000 admin/admin or user/user
Keycloak http://localhost:8080 admin/admin

Demo

  • The gif below shows an admin logging in and adding one movie using the wizard feature:

    demo-admin

  • The gif below shows a user logging in using his Github account; then he changes his avatar and comment a movie:

    demo-user-github

Testing movies-api endpoints

We can manage movies by accessing directly movies-api endpoints using the Swagger website or curl. For the secured endpoints like POST /api/movies, PUT /api/movies/{id}, DELETE /api/movies/{id}, etc, we need to inform an access token issued by Keycloak.

Getting Access Token

  • Open a terminal.

  • Run the following commands to get the access token:

    ACCESS_TOKEN="$(curl -s -X POST \
      "http://localhost:8080/realms/company-services/protocol/openid-connect/token" \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "username=admin" \
      -d "password=admin" \
      -d "grant_type=password" \
      -d "client_id=movies-app" | jq -r .access_token)"
    
    echo $ACCESS_TOKEN
    

    Note: In jwt.io, we can decode and verify the JWT access token.

Calling movies-api endpoints using curl

  • Trying to add a movie without access token:

    curl -i -X POST "http://localhost:9080/api/movies" \
      -H "Content-Type: application/json" \
      -d '{ "imdbId": "tt5580036", "title": "I, Tonya", "director": "Craig Gillespie", "year": 2017, "poster": "https://m.media-amazon.com/images/M/MV5BMjI5MDY1NjYzMl5BMl5BanBnXkFtZTgwNjIzNDAxNDM@._V1_SX300.jpg"}'
    

    It should return:

    HTTP/1.1 401
    
  • Trying again to add a movie, now with access token (obtained at #getting-access-token):

    curl -i -X POST "http://localhost:9080/api/movies" \
      -H "Authorization: Bearer $ACCESS_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{ "imdbId": "tt5580036", "title": "I, Tonya", "director": "Craig Gillespie", "year": 2017, "poster": "https://m.media-amazon.com/images/M/MV5BMjI5MDY1NjYzMl5BMl5BanBnXkFtZTgwNjIzNDAxNDM@._V1_SX300.jpg"}'
    

    It should return:

    HTTP/1.1 201
    {
      "imdbId": "tt5580036",
      "title": "I, Tonya",
      "director": "Craig Gillespie",
      "year": "2017",
      "poster": "https://m.media-amazon.com/images/M/MV5BMjI5MDY1NjYzMl5BMl5BanBnXkFtZTgwNjIzNDAxNDM@._V1_SX300.jpg",
      "comments": []
    }
    
  • Getting the list of movies. This endpoint does not requires access token:

    curl -i http://localhost:9080/api/movies
    

    It should return:

    HTTP/1.1 200
    [
      {
        "imdbId": "tt5580036",
        "title": "I, Tonya",
        "director": "Craig Gillespie",
        "year": "2017",
        "poster": "https://m.media-amazon.com/images/M/MV5BMjI5MDY1NjYzMl5BMl5BanBnXkFtZTgwNjIzNDAxNDM@._V1_SX300.jpg",
        "comments": []
      }
    ]
    

Calling movies-api endpoints using Swagger

  • Access movies-api Swagger website, http://localhost:9080/swagger-ui.html.

  • Click Authorize button.

  • In the form that opens, paste the access token (obtained at getting-access-token) in the Value field. Then, click Authorize and Close to finalize.

  • Done! We can now access the secured endpoints.

Useful Commands

  • MongoDB

    List all movies:

    docker exec -it mongodb mongosh moviesdb
    db.movies.find()
    

    Type exit to get out of MongoDB shell

Shutdown

  • To stop movies-api and movies-ui, go to the terminals where they are running and press Ctrl+C;

  • To stop and remove docker containers, network and volumes, go to a terminal and, inside springboot-react-keycloak root folder, run the command below:

    ./shutdown-environment.sh
    

How to upgrade movies-ui dependencies to latest version

  • In a terminal, make sure you are in springboot-react-keycloak/movies-ui folder;

  • Run the following commands:

    npm upgrade
    npm i -g npm-check-updates
    ncu -u
    npm install
    

springboot-react-keycloak's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

springboot-react-keycloak's Issues

add server port to spring configuration

First of all, thanks, super nice approach.

I realized you are missing the following for the local dev environment, so it does not collide spring boot port with kc

Under application.yml for spring services you could add the following, to launch the react dashboard directly

server: port: 9080

[2372] Failed to execute script docker-compose

Just tried to run the docker-compose command and got this error:

docker-compose up -d
Traceback (most recent call last):
File "urllib3/connectionpool.py", line 670, in urlopen
File "urllib3/connectionpool.py", line 392, in _make_request
File "http/client.py", line 1255, in request
File "http/client.py", line 1301, in _send_request
File "http/client.py", line 1250, in endheaders
File "http/client.py", line 1010, in _send_output
File "http/client.py", line 950, in send
File "docker/transport/unixconn.py", line 43, in connect
FileNotFoundError: [Errno 2] No such file or directory

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "requests/adapters.py", line 439, in send
File "urllib3/connectionpool.py", line 726, in urlopen
File "urllib3/util/retry.py", line 410, in increment
File "urllib3/packages/six.py", line 734, in reraise
File "urllib3/connectionpool.py", line 670, in urlopen
File "urllib3/connectionpool.py", line 392, in _make_request
File "http/client.py", line 1255, in request
File "http/client.py", line 1301, in _send_request
File "http/client.py", line 1250, in endheaders
File "http/client.py", line 1010, in _send_output
File "http/client.py", line 950, in send
File "docker/transport/unixconn.py", line 43, in connect
urllib3.exceptions.ProtocolError: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "docker/api/client.py", line 214, in _retrieve_server_version
File "docker/api/daemon.py", line 181, in version
File "docker/utils/decorators.py", line 46, in inner
File "docker/api/client.py", line 237, in _get
File "requests/sessions.py", line 543, in get
File "requests/sessions.py", line 530, in request
File "requests/sessions.py", line 643, in send
File "requests/adapters.py", line 498, in send
requests.exceptions.ConnectionError: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "docker-compose", line 3, in
File "compose/cli/main.py", line 81, in main
File "compose/cli/main.py", line 199, in perform_command
File "compose/cli/command.py", line 60, in project_from_options
File "compose/cli/command.py", line 152, in get_project
File "compose/cli/docker_client.py", line 41, in get_client
File "compose/cli/docker_client.py", line 170, in docker_client
File "docker/api/client.py", line 197, in init
File "docker/api/client.py", line 221, in _retrieve_server_version
docker.errors.DockerException: Error while fetching server API version: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))
[2372] Failed to execute script docker-compose

Cannot access api via postman

Hi Ivan, you did a great job with this demo app it has helped me a lot. Congrats!
But I do face a problem. I just cannot hit the endpoint via postman.
When I do not provide a token I get a 400 bad request with an HTML response in the body saying:

Invalid parameter: redirect_uri

When I try to call the api after I receive a token I get a 401 unauthorized. Any hint what can go wrong ? Maybe some Keycloak configuration ?

Thanks in advance!

Upgrading to Keycloak 17 results in 401 Auth issue when listing Realms

Upgrading to Keycloak 17 results in 401 Auth issue when listing Realms.

I know this is more of a wish then a defect, but I've been using this project a test and would like to upgrade to Keycloak 17 and have issues.

When I find out why I get the Auth issue with 17 I will feed it back here, and hope to create a PR to update this project.

Verifying JWT tokens in Resource server

Hello @ivangfr , as far as I know there are 2 ways to verify the JWT token
i) Forward the token to Auth server for verification
ii) Verify the token in Resource server itself using public key

In this project you have used the first way, is it possible to use the second way?
If yes could you please let me know.

How to create user extra after first time login keycloak?

First, thank you for the code. It really helps me.
Sorry if this is a newbie question.
I've tried to understand how do you create the user extra such avatar after login at keycloak but I still don't understand. Can you explain a little bit?

Thanks

Thank you so much this great example project!

Best Regards

Bela

Revoking Token in Keycloak

Hello @ivangfr ,

Firstly, great project! loved it. It covers everything from the backend and front end.
However, I had some queries regarding Keycloak in your project.

As far as I have done research the only API to revoke a token is the /revoke API which looks like this

http://localhost:8080/realms/<realm-name>/protocol/openid-connect/revoke
Along with this URL we will also be using clientId,client-secret, token_type and the actual token we want to revoke.

My query is, if we use the PKCE apporoach there is no client-secret ,so how do we revoke the token since the client secret is not optional

The same query applies for the introspect endpoint as well

http://localhost:8080/realms/<realm-name>/protocol/openid-connect/token/introspect

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.