{"id":14986546,"url":"https://github.com/phillwatson/one-stop","last_synced_at":"2026-01-22T22:02:27.633Z","repository":{"id":226466271,"uuid":"588069620","full_name":"phillwatson/one-stop","owner":"phillwatson","description":"A workbench for integrating with GoCardless and Yapily open banking APIs","archived":false,"fork":false,"pushed_at":"2026-01-18T11:50:50.000Z","size":4262,"stargazers_count":1,"open_issues_count":12,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-18T18:39:36.925Z","etag":null,"topics":["docker","event-driven","graal-native","graalvm","kafka","microservices","open-api","open-banking","openid-connect","postgres","quarkus","rest"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/phillwatson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":"audit-service/README.md","citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-01-12T08:54:27.000Z","updated_at":"2026-01-18T11:43:13.000Z","dependencies_parsed_at":"2024-04-14T13:29:50.014Z","dependency_job_id":"81831696-9751-476e-9766-bffd4d0835f5","html_url":"https://github.com/phillwatson/one-stop","commit_stats":{"total_commits":609,"total_committers":3,"mean_commits":203.0,"dds":"0.11001642036124792","last_synced_commit":"fbfbf80f9c0ff903edfe210d465cdcecb082a430"},"previous_names":["phillwatson/one-stop"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/phillwatson/one-stop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phillwatson%2Fone-stop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phillwatson%2Fone-stop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phillwatson%2Fone-stop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phillwatson%2Fone-stop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phillwatson","download_url":"https://codeload.github.com/phillwatson/one-stop/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phillwatson%2Fone-stop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28672316,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T20:48:19.482Z","status":"ssl_error","status_checked_at":"2026-01-22T20:48:14.968Z","response_time":144,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","event-driven","graal-native","graalvm","kafka","microservices","open-api","open-banking","openid-connect","postgres","quarkus","rest"],"created_at":"2024-09-24T14:13:07.032Z","updated_at":"2026-01-22T22:02:27.611Z","avatar_url":"https://github.com/phillwatson.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# One-Stop\n\n---\nA PoC for interacting with the Open-Banking APIs. Allows users connect to\ntheir bank accounts and view the transactions from multiple accounts in an\naggregated fashion.\n\n## Architecture\nOne-Stop has been designed using an event-driven, microservice architecture,\nwhere each service has a specific area of responsibility. However, to keep the\nPoC build simple, the Maven model of parent POM and sub-modules has been\nadopted; with each module having the same version as the parent. Each service\nmodule will generate its own Docker Image.\n\n![Architecture](./docs/one-stop-arch.png)\n\nAn alternative implementation is presented in the branch \"modulith\". That\nimplementation demonstrates how the same application can be built, and maintained,\nas a monolith application.\n\n### Structure\nThe project consists of a parent POM with a number of sub-modules. The sub-modules\nconsist of services (that provide client functionality) and libraries (that provide\ninternal functionality shared by services and other libraries).\n\nIn general, and to reduce clutter, the library modules are grouped under the parent\nmodule `lib-module`. The exception to this is the event libraries; which are grouped\nunder the parent module `event-module`.\n\nAs much as possible, framework specific code (e.g. Quarkus) is kept to the outer\nlayers of the application. This is to allow the services to be easily ported to\nother frameworks (e.g. Micronauts, Spring Boot). Where framework specific code has\nbeen used, it's exposure to the rest of the application has been minimised. For\nexample; the repository classes are \"masked\" by a simple facade (or adaptor) class,\nrather than exposing a framework specific repository interface.\n\n#### Client\nA simple browser UI, written in React, to demonstrate the back-end functionality.\nOne important aspect of this UI is the authentication. Requests are authenticated\nby JWT tokens; signed by rotating private keys and passed in Secure, HTTP-Only,\nStrict Same-Site cookies, without any intervention required by the UI. It also\ndemonstrates the use of XSRF tokens to protect against Cross-Site Request Forgery\nattacks.\n\n#### User Service\nResponsible for on-boarding and managing internal user accounts. Provides OpenID\nConnect authentication. Generates and renews JWT and XSRF tokens; signed with rotating\nprivate keys.\n\n#### Rail Service\nResponsible for communicating with external banking rail services; managing and\ndownloading account information.\n\n#### Notification Service\nResponsible for issuing notifications to users and administrators; via email and\nREST API. Generates email and notifications in the user's locale.\n\n#### Audit Service\nResponsible for recording audit events.\n\n## Documentation\nThe majority of code contains in-line documentation, and most of the sub-modules\ncontain their own README.md file; intended to provide information specific to that\nmodule.\n\nSome sequence diagrams have been included, in the docs folder, to illustrate the\nimplementation of those more complex areas of the application. Also included is a\nPostman collection to allow the manual testing of the application.\n\nIn addition to this documentation, comprehensive unit and integration tests provide\na good source of information on how the application is intended to be used.\n\n## Docker Configuration\nThe following must be added to `environment:` section of the docker-compose.yaml\nservices. This can be also be achieved by creating a docker-compose-override.yaml.\n\n### User Services\n#### Cross-Site Request Forgery\nCross-Site Request Forgery (XSRF) is avoided by the transfer of a random token\ngenerated using the configured XSRF secret (shared by all services).\n```yaml\n# the secret used to generate and verify the XSRF token\nONE_STOP_AUTH_XSRF_SECRET: \"\u003cany string value 18+ chars\u003e\"\n```\n\n#### OpenID Connect Auth Providers\nThe following OpenID Connect Auth Providers are supported, if fully configured:\n- Google\n- GitLab\n- GitHub\n- LinkedIn\n- Apple\n\nTheir configuration consists, generally, of the client ID and secret you obtain\nfrom them. This requires the registration of an \"application\"; which is often no\nmore than a configuration within your account held with the provider.\n\nThose Auth Providers for which the configuration properties are not provided cannot\nbe used for Open ID Connect authentication.\n```yaml\n# authenticate with Google Open-ID Connect\nONE_STOP_AUTH_OPENID_GOOGLE_CLIENT_ID: \"\u003cthe client id issued by Google\u003e\"\nONE_STOP_AUTH_OPENID_GOOGLE_CLIENT_SECRET: \"\u003cthe secret issued by Google\u003e\"\n\n# authenticate with GitHub Open-ID Connect\nONE_STOP_AUTH_OPENID_GITHUB_CLIENT_ID: \"\u003cthe client id issued by GitHub\u003e\"\nONE_STOP_AUTH_OPENID_GITHUB_CLIENT_SECRET: \"\u003cthe secret issued by GitHub\u003e\"\n\n# authenticate with GitLab Open-ID Connect\nONE_STOP_AUTH_OPENID_GITLAB_CLIENT_ID: \"\u003cthe client id issued by GitLab\u003e\"\nONE_STOP_AUTH_OPENID_GITLAB_CLIENT_SECRET: \"\u003cthe secret issued by GitLab\u003e\"\n\n# authenticate with LinkedIn Open-ID Connect\nONE_STOP_AUTH_OPENID_LINKEDIN_CLIENT_ID: \"\u003cthe client id issued by LinkedIn\u003e\"\nONE_STOP_AUTH_OPENID_LINKEDIN_CLIENT_SECRET: \"\u003cthe secret issued by LinkedIn\u003e\"\n\n# authenticate with Apple Open-ID Connect\nONE_STOP_AUTH_OPENID_APPLE_TEAM_ID: \"\u003cTeam id issued by Apple\u003e\"\nONE_STOP_AUTH_OPENID_APPLE_CLIENT_ID: \"\u003cApp client id issued by Apple\u003e\"\nONE_STOP_AUTH_OPENID_APPLE_KEY_ID: \"\u003cApp key id issued by Apple\u003e\"\n\n# the Private Key PEM used to sign data sent to Apple Open-ID Connect\nONE_STOP_AUTH_OPENID_APPLE_PRIVATE_KEY: \"\u003cthe private key in PEM form\u003e\"\n```\n\n### Rail Service\nThe application relies upon the bank data provided by the Nordigen service\n(now owned by GoCardless). Sign-up and access is free:\nhttps://gocardless.com/bank-account-data/ \n\nThe application also supports Yapily. Sandbox sign-up and access is free:\nhttps://docs.yapily.com/\n```yaml\n# the secret used to generate and verify the XSRF token\nONE_STOP_AUTH_XSRF_SECRET: \"\u003cany string value 18+ chars - must be same as user service\u003e\"\n\n# the Nordigen/GoCardless authentication keys\nONE_STOP_RAILS_SECRET_ID: \"\u003cthe secret ID issued by Nordigen\u003e\"\nONE_STOP_RAILS_SECRET_KEY: \"\u003cthe secret issue by Nordigen\u003e\"\n\n# the Yapily authentication keys\nONE_STOP_YAPILY_SECRET_ID: \"\u003cthe secret ID issued by Yapily\u003e\"\nONE_STOP_YAPILY_SECRET_KEY: \"\u003cthe secret issue by Yapily\u003e\"\n```\n\n### Notification Service\nIn order to use the notification service, you will need to obtain an API key from\nBrevo (previously known as Send-In-Blue). Sign-up and access is free:\nhttps://www.brevo.com/\n```yaml\n# the secret used to generate and verify the XSRF token\nONE_STOP_AUTH_XSRF_SECRET: \"\u003cany string value 18+ chars - must be same as user service\u003e\"\n\n# the Brevo (SendInBlue) Email-Service key\nONE_STOP_EMAIL_API_KEY: \"\u003cthe secret issue by Brevo (previously Send-In-Blue)\u003e\"\n\n# to disable the sending of emails - default false\nONE_STOP_EMAIL_DISABLED: \"true\"\n```\n\n## Gateway for 3rd Party Callbacks\nThe application uses the `ONE_STOP_GATEWAY_OPEN_PORT` to receive callbacks from\n3rd party services. It is used by the Rails service to receive callbacks from\nNordigen/GoCardless and Yapily when obtaining users' consent to access their bank\ndetails. It is also used by the User service to receive callbacks from OpenID\nConnection Providers; e.g., Google, GitHub, GitLab and Apple.\n\nThis port must be open on the router to the outside world and forwarded to the\ndevice IP and port on which the application is running. The application is able\nto determine the IP address on which the router is listening (see the class\n`com.hillayes.commons.net.Network`).\n```yaml\n# the http schema to use (http or https)\nONE_STOP_GATEWAY_SCHEME: \"http\"\n\n# the port exposed on the router to the outside world\n# used by callback from 3rd party services (e.g. rails)\nONE_STOP_GATEWAY_OPEN_PORT: \"9876\"\n```\n\n## To Build and Start Docker Images\n### Build Parameters\nBy default, the build does not run unit-tests. To run the unit-tests, add\nthe following parameter to the build command:\n```shell\nmvn clean package -Ptest\n```\nBy default, the client is not built. To build the client, add the following\nparameter to the build command:\n```shell\nmvn clean package -Pclient\n```\n\nBy default, the docker images use JVM base images. To build native docker\nimages, add the following parameter to the build command:\n```shell\nmvn clean package -Dnative\n```\nCombinations of these can be used.\n\n### Building Client Docker Image\nThe client docker image is not built by the maven POM. To build the client create\na docker-compose-override.yaml and include the following - any existing docker\nimage should be deleted first:\n```yaml\nservices:\n  client:\n    image: one-stop/client:1.0.0-SNAPSHOT\n    build:\n      context: ./client\n```\nThen run the build and start the docker images (with client image build):\n```shell\nmvn clean package -Pclient\ndocker compose up -d --build\n```\n\n#### Notes on Native Build for Arm64 / Aarch64\nBuilding native images on Linux Intel (x86_64) is just a matter of using the\n`-Dnative` build arg. Building native images for Arm64 or Aarch64 architectures\nrequires a little more effort. Here are the steps I've taken to achieve this:\n\nFirstly, I'm using an Apple Mac M1 pro (a gift from a previous employer).\n1. Install GraalVM JVM. I use `sdkman` to manage my Java installations:\n   1. `sdk install java 21.0.2-graalce`\n   2. Select the new installation as \"default\"\n2. Set the environment property `GRAALVM_HOME` to locate the VM installation:\n   1. `export GRAALVM_HOME=$HOME/.sdkman/candidates/java/21.0.2-graalce` \n3. You should now be able to build the native images using the command line:\n   1. `mvn clean package -Dnative`\n\nThe POMs have been configured to set additional environment variables when\nperforming a native build on Aarch64 architecture. See the \"arm64\" profile in\nthe root `pom.xml`. Note, it is in this profile that the tag for the generated\ncontainer image is set.\n\n## Debugging Docker Images\nAll non-native docker images are built with remote JVM debugging enabled. In\norder to connect to the images the debug port 5005 must be exposed. Each\ncontainer should expose that internal port on a unique port - to avoid clashes.\n```yaml\n  user-service:\n    image: one-stop/user-service:1.0.0-SNAPSHOT\n    ports:\n      - \"8181:8080\"\n      - \"5001:5005\"\n```\n\n## Manually Pushing Docker Images to GitHub Repo\n### login to GitHub repo\nWe need to login to our GitHub account using a Personal Access Token (classic). That\ntoken needs to be created in your GitHub account:\n1. Go to your GitHub account settings\n2. Navigate to Developer settings \u003e Personal access tokens (https://github.com/settings/tokens)\n3. Click on \"Generate new token\" and select \"Generate new token (classic)\"\n4. Give the new token a \"note\" describing what it will be for\n5. In the scopes, select the following\n   1. write:packages\n   2. delete:packages - if you will later want to use the token to delete\n6. Click \"Generate token\"\n7. Note the token ID - do it now, you won't be able to see it again \n```bash\ndocker login --username \u003cusername\u003e --password \u003caccess-token-id\u003e ghcr.io\n```\n### push image\n```bash\ndocker push \u003cfully-qualified-image-name\u003e\n```\nThe fully-qualified image name consists of the repo, username, image-name and tag.\nFor example; `ghcr.io/phillwatson/one-stop-user-service:1.0.0-SNAPSHOT`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphillwatson%2Fone-stop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphillwatson%2Fone-stop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphillwatson%2Fone-stop/lists"}