Skip to main content

Face Detection Login System

Oracle APEX 24.2 · Complete Technical Documentation

Updated 9 min readMarkdown
Face Detection Login System

This document covers the complete implementation of a biometric face detection login system built inside Oracle APEX 24.2. The system replaces or supplements the standard username/password login with a real-time webcam-based face recognition flow, powered entirely by face-api.js running in the browser — no external paid API required.

1.1 Technology Stack

Component

Detail

UI Platform

Oracle APEX 24.2

Database

Oracle Database 19c

Face AI Library

face-api.js v0.22.2 (browser-side)

Face Models

TinyFaceDetector + FaceLandmark68 + FaceRecognition

Authentication

Custom APEX Auth Scheme (PL/SQL function)

Data Storage

apex_face_users table (CLOB descriptor)

Login Page

Page 9999 (APEX default login page)

App URL

https://oracleapex.com/ords/f?p=218621:LOGIN

1.2 How It Works — Flow Summary

  • User opens the APEX login page (Page 9999)
  • Two buttons are visible: Register Your Face and Login with Face Detection
  • Registration: user enters username → camera opens → face is auto-captured after 5 confirmed frames → 128-point descriptor saved to DB
  • Login: camera opens → face scanned → descriptor compared against DB via Euclidean distance → if match ≤ 0.5 threshold → login submitted automatically
  • APEX custom auth scheme validates the bypass password and confirms username exists in apex_face_users

2. Database Setup

2.1 Face Users Table

Run this SQL in SQL Workshop → SQL Commands:

CREATE TABLE apex_face_users (
id   NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
username  VARCHAR2(100) NOT NULL,
face_descriptor  CLOB NOT NULL,
created_date     DATE DEFAULT SYSDATE
);

NOTE: face_descriptor stores a 128-element JSON float array like [0.12, -0.34, 0.56, ...]. It is NOT an image — do not use a file upload control to populate it.

2.2 Custom Authentication Function

Run this in SQL Workshop → SQL Commands:

CREATE OR REPLACE FUNCTION custom_face_auth ( p_username IN VARCHAR2, p_password IN VARCHAR2 ) RETURN BOOLEAN IS l_count NUMBER; BEGIN IF p_password = 'FACE_BYPASS_2024' THEN SELECT COUNT(*) INTO l_count FROM apex_face_users WHERE UPPER(username) = UPPER(p_username); RETURN l_count > 0; END IF; RETURN FALSE; END;

3. APEX Authentication Scheme

Go to: Shared Components → Authentication Schemes → Create → From Scratch

Setting

Value

Name

Face Detection Auth

Scheme Type

Custom

Authentication Function

custom_face_auth

After creating, make this scheme Current by clicking "Make Current Scheme" so APEX uses it for all logins.

 

4. Model Files Setup

4.1 Download from GitHub

Download all 6 files from: https://github.com/justadudewhohacks/face-api.js/tree/master/weights

● tiny_face_detector_model-shard1
● tiny_face_detector_model-weights_manifest.json
● face_landmark_68_model-shard1
● face_landmark_68_model-weights_manifest.json
● face_recognition_model-shard1
● face_recognition_model-shard2 ← critical, often missed
● face_recognition_model-weights_manifest.json

4.2 Upload to APEX

Go to: Shared Components → Static Application Files → Upload

● Create a folder called models/
● Upload all 7 files into the models/ folder
● Reference path: #APP_FILES#models/

IMPORTANT: face_recognition_model has TWO shards (shard1 and shard2). Missing shard2 causes a tensor shape error and models will not load.

5. APEX Page 9999 Setup

5.1 Existing Items (Pre-built by APEX)

Item

Type

Region

Face Detection (Content Body)

P9999_USERNAME

Text Field

P9999_PASSWORD

Text Field (Password)

P9999_REMEMBER

Checkbox

LOGIN button

Submit → request = LOGIN

5.2 New Buttons to Create

Button Name

Configuration

Login with Face Detection

Action: Defined by Dynamic Action → DA calls openFaceDetection()

Register Your Face

Action: Defined by Dynamic Action → DA calls openRegisterModal()

5.3 Dynamic Actions

For each button create a Dynamic Action:

●        Event: Click
●        Selection Type: Button → select the button
●        TRUE Action: Execute JavaScript Code
○        Login button → Code: openFaceDetection()
○        Register button → Code: openRegisterModal()
Fire on Page Load: NO

5.4 HTML Regions to Create

Region Name

Configuration

Registration Modal

Type: Static Content | Position: Content Body | Paste registration HTML

Verification Overlay

Type: Static Content | Position: Content Body | Paste verification HTML

6. AJAX Callback Processes

Go to: Page 9999 → Processing → right-click → Create Process → AJAX Callback

6.1 REGISTER_FACE - Saves a new face descriptor or updates existing one:

DECLARE l_username VARCHAR2(100) := apex_application.g_x01; l_descriptor CLOB := apex_application.g_x02; l_count NUMBER; BEGIN SELECT COUNT(*) INTO l_count FROM apex_face_users WHERE username = l_username; IF l_count > 0 THEN UPDATE apex_face_users SET face_descriptor = l_descriptor, created_date = SYSDATE WHERE username = l_username; ELSE INSERT INTO apex_face_users (username, face_descriptor) VALUES (l_username, l_descriptor); END IF; COMMIT; htp.p('{"status":"success","username":"' || l_username || '"}' ); EXCEPTION WHEN OTHERS THEN htp.p('{"status":"error","message":"' || SQLERRM || '"}' ); END;

6.2 VERIFY_FACE Returns all stored face descriptors for JS-side Euclidean comparison:

DECLARE l_json CLOB := '['; l_first BOOLEAN := TRUE; BEGIN FOR r IN (SELECT username, face_descriptor FROM apex_face_users) LOOP IF NOT l_first THEN l_json := l_json || ','; END IF; l_json := l_json || '{"username":"' || r.username || '","descriptor":' || r.face_descriptor || '}'; l_first := FALSE; END LOOP; l_json := l_json || ']'; apex_util.set_session_state('P9999_STATUS', l_json); htp.p(l_json); END;

6.3 DO_FACE_LOGIN Sets session state before page submit:

BEGIN apex_util.set_session_state('P9999_USERNAME', apex_application.g_x01); htp.p('{"status":"ok"}'); END;

7. JavaScript — Execute when Page Loads

Paste the entire block below into: Page 9999 → JavaScript → Execute when Page Loads

7.1 Model Loading

const MODEL_URL = 'https://oracleapex.com/ords/r/unionsys/face-detection/files/static/v1/models'; let cameraStream = null, detectionInterval = null, modelsLoaded = false; const faceScript = document.createElement('script'); faceScript.src = 'https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/dist/face-api.min.js'; faceScript.onload = async function () { await faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL); await faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL); await faceapi.nets.faceRecognitionNet.loadFromUri(MODEL_URL); modelsLoaded = true; console.log('Face models loaded.'); }; document.head.appendChild(faceScript);

7.2 Key Fix — Landmark Accuracy The most important fix for accurate face landmark rendering (eyes, lips, nose in correct position): // After video loads, match canvas dimensions to display size

var displaySize = { width: video.offsetWidth, height: video.offsetHeight }; faceapi.matchDimensions(canvas, displaySize);

// Before drawing detections, results to display size //

var resized = faceapi.resizeResults(det, displaySize); faceapi.draw.drawDetections(canvas, resized); faceapi.draw.drawFaceLandmarks(canvas, resized);

7.3 Login Flow On successful face match, the JS sets the APEX items and submits the login:

apex.item('P9999_USERNAME').setValue(username); apex.item('P9999_PASSWORD').setValue('FACE_BYPASS_2024'); apex.page.submit({ request: 'LOGIN' }); The custom_face_auth function then checks apex_face_users and returns TRUE if username exists.

8. Bugs Fixed Along the Way

Bug Fix
Page submitting on button click HTML buttons default to type="submit" — fixed by adding type="button" to all buttons in HTML regions
openFaceDetection is not defined Functions declared inside onload callback were not global — fixed by changing all to window.functionName = function()
face_recognition_model 404 error shard2 file was missing — fixed by downloading and uploading face_recognition_model-shard2
Tensor shape mismatch error Caused by the missing shard2 — resolved when shard2 was uploaded
Landmarks in wrong position Canvas native size did not match display size — fixed using faceapi.matchDimensions + faceapi.resizeResults
Face captured but not saving to DB Button without type="button" was submitting the page before AJAX completed
Login not working after face match DO_FACE_LOGIN alone does not create session — fixed by submitting the LOGIN request with FACE_BYPASS_2024

9. Complete End-to-End Flow

9.1 Registration Flow

●        User clicks Register Your Face button
●        openRegisterModal() fires via Dynamic Action
●        Modal opens — Step 1 shown: username input + Continue button
●        User enters username and clicks Continue
●        startRegisterCamera() fires — webcam starts
●        Step 2 shown: ring with live video feed
●        faceapi detects face every 200ms using TinyFaceDetector
●        faceapi.matchDimensions ensures landmarks render accurately
●        5 confirmed frames fill the progress dots
●        AUTO CAPTURE fires — no button click needed
●        Ring turns green with pulsing glow
●        REGISTER_FACE AJAX process called with username (x01) + descriptor JSON (x02)
●        PL/SQL inserts or updates apex_face_users table
●        Success message shown, modal closes after 2 seconds

9.2 Login Flow

● User clicks Login with Face Detection button
● openFaceDetection() fires via Dynamic Action
● Verification overlay opens with violet/indigo pulsing ring
● Webcam starts, faceapi runs every 200ms
● 5 confirmed frames fill progress dots (amber color)
● VERIFY_FACE AJAX called — PL/SQL returns all stored descriptors as JSON
● JS loops through stored descriptors and calculates Euclidean distance
● Best match checked against threshold 0.5
● Match found → ring turns green → Welcome message shown
● After 1.8s: P9999_USERNAME = matched username, P9999_PASSWORD = FACE_BYPASS_2024, page submitted
● custom_face_auth function runs → checks apex_face_users → returns TRUE → user logged in
● APEX redirects to home page
● No match → ring turns red → Access Denied → overlay closes after 3 seconds

10. Troubleshooting Quick Reference

Symptom Solution
Models not loading Check F12 console for 404 — verify model URL matches static file path in Shared Components
Button does nothing Check Dynamic Action → TRUE Action is Execute JavaScript Code with correct function name
Page jumps back on camera open Button inside HTML region is missing type="button"
Face not detected Improve lighting, move closer, use Chrome browser
Always says Access Denied No data in apex_face_users — register a face first
Login fails after green ring Verify custom_face_auth function exists in DB and Authentication Scheme is set to Current
Landmarks shifted/offset faceapi.matchDimensions or faceapi.resizeResults missing in JS
shard2 404 error Upload face_recognition_model-shard2 to Static Application Files → models/

11. Security Notes

The FACE_BYPASS_2024 password is a shared secret used to bypass normal password auth. For production use, consider rotating this value and storing it in APEX Application Settings rather than hardcoding in JS.

● Face descriptors are stored as plain JSON in CLOB — consider encrypting at rest for production
● Euclidean threshold of 0.5 balances accuracy vs security — lower = stricter
● For multi-user environments, register multiple face angles per user for better accuracy
● HTTPS is required for webcam access in all modern browsers
● This implementation is suitable for internal tools and demos — for banking/high security apps, use a dedicated biometric SDK

Thank You for your time.

If you need any help or have any questions, feel free to leave a comment or contact me via email.

Email: arfaats999@gmail.com

About the author

  • Hi, I'm Arfaat Shaikh, an Oracle APEX Developer with a passion for building scalable, user-friendly enterprise applications. I enjoy solving real-world problems using Oracle APEX, PL/SQL, SQL, REST APIs, and AI-powered solutions. Through this blog, I document practical Oracle APEX tutorials, production-ready solutions, troubleshooting guides, and best practices from my development journey. My goal is to make complex concepts simple and create resources that help developers learn faster and build with confidence. Whether you're just getting started with Oracle APEX or looking for advanced techniques, I hope you'll find something useful here. Happy coding!

No comments yet. Be the first to comment.