Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion obp-api/src/main/resources/props/sample.props.template
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,10 @@ db.url=jdbc:h2:./lift_proto.db;NON_KEYWORDS=VALUE;DB_CLOSE_ON_EXIT=FALSE
## (this needs to be a URL)
hostname=http://127.0.0.1:8080

# Used as the base URL for password reset and email validation links sent via email.
# Set this to your frontend/portal URL so that emails contain the correct link.
portal_external_url=http://localhost:5174

## This is only useful for running the api locally via RunWebApp
## If you use it, make sure this matches your hostname port!
## If you want to change the port when running via the command line, use "mvn -Djetty.port=8080 jetty:run" instead
Expand Down Expand Up @@ -813,7 +817,13 @@ autocomplete_at_login_form_enabled=false
# This involves this OBP-API sending an email to the newly registered email provided by the User and the User clicking on a link in that email
# which results in a field being changed in the database.
# To BYPASS this security features (for local development only), set this property to true to skip the email address validation.
#authUser.skipEmailValidation=false
authUser.skipEmailValidation=false

# Expiry time in minutes for email validation JWT tokens (default: 1440 = 24 hours)
email_validation_token_expiry_minutes=1440

# Expiry time in minutes for password reset JWT tokens (default: 120 = 2 hours)
password_reset_token_expiry_minutes=120

# control the create and access to public views.
# allow_public_views=false
Expand Down
13 changes: 13 additions & 0 deletions obp-api/src/main/resources/props/test.default.props.template
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ write_metrics = false
# --for tests don't set it to 127.0.0.1, for some reason
hostname=http://localhost:8016

# Used as the base URL for password reset and email validation links sent via email.
# Set this to your frontend/portal URL so that emails contain the correct link.
portal_external_url=http://localhost:5174

# Set to true to skip email validation on user signup (default: false)
authUser.skipEmailValidation=false

# Expiry time in minutes for email validation JWT tokens (default: 1440 = 24 hours)
email_validation_token_expiry_minutes=1440

# Expiry time in minutes for password reset JWT tokens (default: 120 = 2 hours)
password_reset_token_expiry_minutes=120

#this is only useful for running the api locally via RunWebApp
#if you use it, make sure this matches your hostname port!
#if you want to change the port when running via the command line, use "mvn -Djetty.port=8089 jetty:run" instead
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ object MessageDocsSwaggerDefinitions
title =titleExample.value,
branchId = branchIdExample.value,
nameSuffix = nameSuffixExample.value,
customerType = "",
parentCustomerId = ""
customerType = Some("INDIVIDUAL"),
parentCustomerId = Some("")
)

val customerAttribute = CustomerAttributeCommons(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2135,8 +2135,7 @@ object SwaggerDefinitionsJSON {
username = usernameExample.value,
password = "String",
first_name = "Simon",
last_name = "Redfern",
validating_application = Some("OBP-Portal")
last_name = "Redfern"
)


Expand Down
116 changes: 108 additions & 8 deletions obp-api/src/main/scala/code/api/util/Glossary.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1114,7 +1114,7 @@ object Glossary extends MdcLoggable {


glossaryItems += GlossaryItem(
title = "Consent_OBP_Flow_Example",
title = "Authentication: Consent OBP Flow Example",
description =
s"""
|#### 1) Call endpoint Create Consent Request using application access (Client Credentials)
Expand Down Expand Up @@ -1355,7 +1355,7 @@ object Glossary extends MdcLoggable {


glossaryItems += GlossaryItem(
title = "Direct Login",
title = "Authentication: Direct Login",
description =
s"""
|Direct Login is a simple authentication process to be used at hackathons and trusted environments:
Expand Down Expand Up @@ -2136,7 +2136,7 @@ object Glossary extends MdcLoggable {
""")

glossaryItems += GlossaryItem(
title = "OAuth 1.0a",
title = "Authentication: OAuth 1.0a",
description =
s"""
|The following steps will explain how to connect an instance of the Open Bank Project OAuth Server 1.0a. This authentication mechanism is necessary so a third party application can consume the Open Bank project API securely.
Expand Down Expand Up @@ -2372,7 +2372,7 @@ object Glossary extends MdcLoggable {
{"OAuth2 is allowed on this instance."} else {"Note: *OAuth2 is NOT allowed on this instance!*"}

glossaryItems += GlossaryItem(
title = "OAuth 2",
title = "Authentication: OAuth 2",
description =
s"""
|
Expand Down Expand Up @@ -2581,7 +2581,7 @@ object Glossary extends MdcLoggable {


glossaryItems += GlossaryItem(
title = "Gateway Login",
title = "Authentication: Gateway Login",
description =
s"""
|### Introduction
Expand Down Expand Up @@ -4151,7 +4151,7 @@ object Glossary extends MdcLoggable {
| """.stripMargin)

glossaryItems += GlossaryItem(
title = "OAuth 2.0",
title = "Authentication: OAuth 2.0",
description =
s"""OAuth 2.0, is a framework, specified by the IETF in RFCs 6749 and 6750 (published in 2012) designed to support the development of authentication and authorization protocols. It provides a variety of standardized message flows based on JSON and HTTP.""".stripMargin)

Expand Down Expand Up @@ -5037,6 +5037,105 @@ object Glossary extends MdcLoggable {
)
)

glossaryItems += GlossaryItem(
title = "Email Validation for OBP Local Users",
description =
s"""
|### Overview
|
|When a new OBP local user is created, they may be required to validate their email address before they can log in.
|This is controlled by the `authUser.skipEmailValidation` property (default: `false`).
|
|When email validation is enabled, the user receives an email containing a signed JWT token with a validation link.
|The user clicks the link, and the App (portal) extracts the token and calls the API to complete the validation.
|
|### Props
|
|The following properties are involved:
|
|- `authUser.skipEmailValidation` — Set to `true` to skip email validation entirely (default: `false`). Currently: `${APIUtil.getPropsAsBoolValue("authUser.skipEmailValidation", false)}`
|- `portal_external_url` — **Required.** The base URL of your frontend/portal application. Used to construct the validation link in the email. For example: `portal_external_url=https://your-portal.example.com`. Currently: `${APIUtil.getPropsValue("portal_external_url", "not set")}`
|- `email_validation_token_expiry_minutes` — Expiry time for the validation JWT token in minutes (default: `1440` i.e. 24 hours). Currently: `${APIUtil.getPropsAsIntValue("email_validation_token_expiry_minutes", 1440)}`
|
|### Step 1: User Creation
|
|A user can be created via:
|
|**POST /obp/v6.0.0/users** (no authentication required)
|
|Request body:
|
| {
| "username": "user@example.com",
| "password": "Str0ng!Password",
| "first_name": "Jane",
| "last_name": "Doe",
| "email": "user@example.com"
| }
|
|If `authUser.skipEmailValidation=false`, the API will:
|
|1. Create the user with `validated=false`
|2. Generate a signed JWT token containing the user's unique ID as the subject, with a configurable expiry
|3. Construct a validation link: `{portal_external_url}/user-validation?token={JWT}`
|4. Send an email to the user with the validation link
|
|The user or the legacy Lift signup form can also trigger validation emails. In all cases, the same JWT-based token is used.
|
|### Step 2: Email Validation
|
|**POST /obp/v6.0.0/users/email-validation** (no authentication required)
|
|Request body:
|
| {
| "token": "eyJhbGciOiJIUzI1NiJ9..."
| }
|
|Response (201):
|
| {
| "user_id": "5995d6a2-01b3-423c-a173-5481df49bdaf",
| "email": "user@example.com",
| "username": "user@example.com",
| "provider": "https://your-api.example.com",
| "validated": true,
| "message": "Email validated successfully"
| }
|
|Error responses:
|
|- **400** — Invalid JSON format or empty token
|- **404** — Invalid or expired JWT token (bad signature, expired, or user not found)
|- **400** — User email is already validated
|
|This endpoint:
|
|1. Verifies the JWT signature (HMAC) and checks the expiry time
|2. Extracts the unique ID from the JWT subject
|3. Looks up the user by unique ID
|4. Sets the user's validated status to `true`
|5. Resets the unique ID (invalidating the token — it is single-use)
|6. Grants default entitlements to the user
|
|### Token Security
|
|- The token is a **signed JWT** (HMAC-SHA256) — it cannot be forged without the server's shared secret.
|- The token has a **configurable expiry** (default: 24 hours) set via `email_validation_token_expiry_minutes`.
|- The token is **single-use** — after validation, the unique ID is reset, so the same token cannot be used again.
|
|### Typical App Flow
|
|1. User submits registration form
|2. App calls POST /obp/v6.0.0/users
|3. App shows "Check your email for a validation link"
|4. User clicks link in email, App opens at `/user-validation?token={JWT}`
|5. App extracts the token from the URL query parameter
|6. App calls POST /obp/v6.0.0/users/email-validation with the token
|7. App shows "Email validated successfully. Please log in."
|
|""")

glossaryItems += GlossaryItem(
title = "Password Reset for OBP Local Users",
description =
Expand Down Expand Up @@ -5073,6 +5172,7 @@ object Glossary extends MdcLoggable {
|
|- The response is always the same whether or not the user exists. This prevents user enumeration.
|- If the user exists, is validated, and the email matches, a reset email is sent containing a link with a reset token.
|- The reset link base URL is constructed from the `portal_external_url` props value (currently: `${APIUtil.getPropsValue("portal_external_url", "not set")}`). This must be set to your frontend/portal URL so that reset emails contain the correct link.
|- The App should present a form asking for username and email, call this endpoint, and then show a message saying "Check your email for a reset link."
|
|### Step 2: Complete Password Reset
Expand Down Expand Up @@ -5101,7 +5201,7 @@ object Glossary extends MdcLoggable {
|
|Notes:
|
|- The token is a signed JWT with a configurable expiry (default: 120 minutes). The server-side expiry can be configured with the `password_reset_token_expiry_minutes` property.
|- The token is a signed JWT with a configurable expiry (default: 120 minutes). The server-side expiry can be configured with the `password_reset_token_expiry_minutes` property (currently: `${APIUtil.getPropsAsIntValue("password_reset_token_expiry_minutes", 120)}` minutes).
|- The token comes from the reset email URL. The App should extract the token from the URL path (everything after `/user_mgt/reset_password/`) and URL-decode it before sending it to this endpoint.
|- The token is single-use. Once the password is reset, the token is invalidated. An expired token will also be rejected.
|
Expand Down Expand Up @@ -5148,7 +5248,7 @@ object Glossary extends MdcLoggable {
""")

glossaryItems += GlossaryItem(
title = "Credential Checking Flow",
title = "Authentication: Credential Checking Flow",
description =
s"""
|### Overview
Expand Down
Loading
Loading