Skip to content
All work

Banking identityIn production

A customer identity that is whole in every system, or in none.

Customer identity for the Serbian retail bank of a European banking group: one service that creates, changes and retires a customer's digital identity across the identity provider, the authenticator platform, the token service and transaction signing, with an audit trail for every change.

Role
Backend engineer, owned several services
Client
Retail bank, European banking group
Engagement
Through a software consultancy
Status
Running in production

Context

A banking customer's identity lives in four places. The login account sits in the identity provider, the authenticator devices in a separate platform, device enrolments in the bank's token service, and signing with the transaction signing service. If creating a customer fails halfway, the customer is stuck, and support cannot see why.

Constraints

  • One identity spread across four systems, with no transaction that spans them.
  • Every change has to be explainable later, to support and to auditors.
  • Support staff need to unblock a locked customer without an engineer.
  • Every service goes through the bank's security review.
Customer identity: creation that fails halfway
  1. Request validatedRequired fields and uniqueness checked before any system is touched
  2. Authenticator user createdCustomer registered on the device platform
  3. Login account failedIdentity provider rejected the account
  4. Authenticator user removedCompensating step undoes the earlier one
  5. Operation loggedRequest and outcome written to the identity audit log
IDNew customer, rolled back cleanly0 of 5

My responsibility

Backend engineer in the bank's delivery team, working through a software consultancy. Owned several services end to end, including the customer identity service described here.

Architecture

Identity lifecycle
A Spring Boot service on Java 21 that creates, updates and retires a customer's identity across Keycloak (the OAuth 2.0 and OIDC provider for the banking app), an HID authentication platform for authenticator devices, the bank's security token service and the signing service.
Audit trail
Every create, update and delete is written to an identity log with the request that caused it, in a schema managed by Liquibase migrations.
Support tooling
Paginated customer search and export, account status changes, credential reset, and reading or resetting a customer's signing OTP counter.
Service security
The service is an OAuth2 resource server validating JWTs. Its own calls to Keycloak and the authenticator platform use client-credential tokens cached with Caffeine.
Engineering hygiene
Integration tests against PostgreSQL in Testcontainers, a typed HTTP client per external system, formatting enforced in a pre-commit hook, and automated semantic release tagging.

Critical decisions

Compensating steps

No transaction spans the four systems, so creation runs as ordered steps, each with a way back. If a later step fails, the earlier ones are undone and no half-created customer is left behind.

Validate before touching anything

Required fields and uniqueness are checked before the first external call, so most failures never reach the other systems at all.

Retired accounts are disabled

Retiring a customer disables the login account and keeps it, so the history stays available to support and auditors.

Log the request behind each change

The identity log keeps the request that caused each change, so "why is this customer in this state" has an answer.

When things go wrong

WhenWhat happens
The login account fails after the authenticator user existsAn async listener removes the authenticator user again; the customer can be created cleanly on retry.
A customer is retiredDevices are unassigned, token enrolments removed and the login disabled, with the history kept.
A customer is locked out of signingSupport reads or resets the OTP counter through the operator API.
An external system rejects a callA typed error for that system is returned and logged, so support sees which system failed.

Result

Running in production behind the bank's mobile and web banking app.

Customers on the identity platform

“I worked with Nikola on the same team developing an enterprise banking solution. He is a skilled Java Spring developer, delivering high quality, scalable and secure code. In addition to his technical skills, he is a collaborative team member who shares his knowledge and expertise.”
Nikola Perisic, Software EngineerFormer teammate on the banking platformView on LinkedIn

Stack

Java 21 · Spring Boot 3 · Keycloak · OAuth 2.0 / OIDC · HID authentication · PostgreSQL · Liquibase · Testcontainers

Next case study

3DIN: geo-verified photo and video capture

Need a system that holds up like this one?

Tell me what's at stake. You'll hear back within one business day, and the first call is 30 minutes, with no obligation.