This post will look at a small web-app with allows to share geolocations between friends, using React and Leaflet at the frontend, Micronaut at the backend, Okta as identity provider, and Heroku for free hosting.

featured

github: https://github.com/fladdimir/locsharex

Update: as of 11/2022 Heroku suspended their free tier offer


Features and UI

Login

To get a first impression without sign-up, 3 pre-defined test-users allow you to quickly see the app in action.

login-screen

Okta-Login

To create your own user, sign-in via Okta, a free Open-ID-Connect identity provider.
(Free for any app as long as there are no more than 15k monthly users.)

okta

The Map

Main screen of the app, where you can see your and your friends’ locations.
Edit your location by dragging the marker onto the map, stop sharing your position, or rely on your device location (consent needed). Filter for username in case of searching for someone specific.

map

Settings

The contact-administration page. Look for not-yet-connected people by username, change the name by which people may find you. Sign-out to try another test-user or check back later.

settings


Technologies

Frontend

Single-page react-app, created with create-react-app and Material-UI. Open-street-maps integration with help of leaflet and react-leaflet.
The create-react-app tooling also includes HTTPS support required for using the geo-location browser api.

Backend

Micronaut, a JVM-based microservice framework, aiming for lower memory footprints and faster start-up times than Spring-Boot, and with support for creating native images with GraalVM (for further start-up time reduction).
Convenient entity persistence with Data Repositories and Hibernate.

Entity-relationship-model, showing the AppUser with it’s app-internal ID, name, (embedded) position, and associated users (m-n self-reference):

erm

Identity Provider

Micronaut Security features support for OpenID Connect-based authentication flows. To link persisted user-entities with actual users authenticating via OIDC, the entity contains information on the identity provider and the sub (user ID from the identity provider). That information is only used once during login to identify the logged-in user before issuing a new JWT. The info from the identity-provider is given only to the database.
Micronaut Security supports different OIDC providers out of the box (e.g. Auth0, AWS-Cognito, Okta, Keycloak).

Overview of the application components and their interaction during OIDC login:

setup

  1. The user decides to login and issues a request to initiate the flow
  2. The response redirects to the identity provider
  3. The IDP login is loaded, where the user enters credentials
  4. Upon successful IDP login, the user is redirected to a callback-endpoint of the app (including a one-time code)
  5. The user issues the callback request (including the one-time code)
  6. The app backend makes a secure back-channel request to exchange the user-related one-time code for an ID-token (together with an app-specific, pre-configured client-secret)
  7. The IDP returns an ID-token containing user information
  8. The app backend creates a new JWT containing the app-internal user ID and returns the JWT as an http-only cookie as part of a redirecting response
  9. The browser then sends the cookie with every API request, allowing the backend to validate the JWT and the permission of the user to access requested resources

During logout the user needs to clear the existing IDP session as well as the app session, which can be achieved by a series of redirects between the logout endpoints.

(In-depth explanation of OAuth 2 and OIDC: presentation.)

Heroku

Heroku is a platform-as-service which offers a simple way to run an app in the cloud, including a free tier of 550 micro instance-hours per month and a postgres-database with a 10k row limit. After 30 mins of inactivity without incoming requests, an instance is put to sleep and stops consuming instance-hours. To quickly respond to a request when waking up an instance, the usage of a native image of the app can be beneficial (reducing the app startup time from ~4.1 seconds to ~0.24s).